HEADER_SLICES = Collections.unmodifiableList(Arrays.asList(CODE_SLICE_SPEC, VERSION_SLICE_SPEC));
+//
+// private int code;
+//
+// private long version;
+//
+// public int getCode() {
+// return code;
+// }
+//
+// public long getVersion() {
+// return version;
+// }
+//
+// public BinarySegmentHeader(int code, long version) {
+// this.code = code;
+// this.version = version;
+// }
+//
+// public static int resolveCode(InputStream in) {
+// return BytesUtils.readInt(in);
+// }
+//
+//
+// public static long resolveVersion(InputStream in) {
+// return BytesUtils.readLong(in);
+// }
+//
+// public static BinarySegmentHeader resolveFrom(InputStream in) {
+// int code = resolveCode(in);
+// long version = resolveVersion(in);
+// return new BinarySegmentHeader(code, version);
+// }
+//
+// public static void writeCode(int code, OutputStream out) {
+// BytesUtils.writeInt(code, out);
+// }
+//
+// public static void writeVersion(long version, OutputStream out) {
+// BytesUtils.writeLong(version, out);
+// }
+//
+// public void writeTo(OutputStream out) {
+// writeCode(code, out);
+// writeVersion(version, out);
+// }
+//
+//
+// public byte[] toBytes() {
+// ByteArrayOutputStream out =new ByteArrayOutputStream();
+// writeTo(out);
+// return out.toByteArray();
+// }
+//
+//
+//
+//}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/BinarySliceSpec.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/BinarySliceSpec.java
new file mode 100644
index 00000000..c98ffcc8
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/BinarySliceSpec.java
@@ -0,0 +1,82 @@
+package com.jd.blockchain.binaryproto;
+
+/**
+ * 表示一个二进制数据片段的格式标准;
+ *
+ *
+ * 一个数据契约的实例输出生成的二进制数据段{@link BinarySegmentHeader}是由一系列小的标准化的数据片段组成;
+ *
+ * @author huanghaiquan
+ *
+ */
+public class BinarySliceSpec {
+
+ private boolean repeatable;
+
+ private int length;
+
+ private boolean dynamic;
+
+ private String name;
+
+ private String description;
+
+ /**
+ * 是否重复多次;true 表示以一个头部表示接下来的片段将重复的次数;
+ *
+ * @return
+ */
+ public boolean isRepeatable() {
+ return repeatable;
+ }
+
+ /**
+ * 字节长度;
+ *
+ * @return
+ */
+ public int getLength() {
+ return length;
+ }
+
+ /**
+ * 长度是动态扩展的;
+ *
+ * @return
+ */
+ public boolean isDynamic() {
+ return dynamic;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ private BinarySliceSpec(String name, String description, boolean repeatable, int length, boolean dynamic) {
+ this.name = name;
+ this.description = description;
+ this.repeatable = repeatable;
+ this.length = length;
+ this.dynamic = dynamic;
+ }
+
+ public static BinarySliceSpec newFixedSlice(int length, String name, String description) {
+ return new BinarySliceSpec(name, description, false, length, false);
+ }
+
+ public static BinarySliceSpec newRepeatableFixedSlice(int length, String name, String description) {
+ return new BinarySliceSpec(name, description, true, length, false);
+ }
+
+ public static BinarySliceSpec newDynamicSlice(String name, String description) {
+ return new BinarySliceSpec(name, description, false, -1, true);
+ }
+
+ public static BinarySliceSpec newRepeatableDynamicSlice(String name, String description) {
+ return new BinarySliceSpec(name, description, true, -1, true);
+ }
+}
\ No newline at end of file
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DConstructor.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DConstructor.java
new file mode 100644
index 00000000..620f6e22
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DConstructor.java
@@ -0,0 +1,25 @@
+package com.jd.blockchain.binaryproto;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Created by zhangshuang3 on 2018/7/19.
+ */
+@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DConstructor {
+
+ /**
+ * 名称;
+ *
+ * 默认为属性的名称;
+ *
+ * @return
+ */
+ String name() default "";
+
+}
+
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContract.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContract.java
new file mode 100644
index 00000000..dee872e3
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContract.java
@@ -0,0 +1,48 @@
+package com.jd.blockchain.binaryproto;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * {@link DataContract} 表示数据契约,用于把一个接口类型声明为一份标准化“数据契约”;
+ *
+ * “数据契约”的定义由类型编码({@link #code()})和属性列表(由标注
+ * {@link DataField}定义)构成,其中属性列表中有唯一一个“主键字段”, 主键字段的值用于标识数据契约实例的唯一性;
+ *
+ * “数据契约”通过属性可以引用其它的“数据契约”(由 {@link DataField#refContract()} = true
+ * 定义),这样便构成了“数据契约”嵌套定义的关系图;
+ *
+ * 当对一个“数据契约”进行二进制序列化输出时,从根对象出发,对关系图中的每一个“数据契约”实例都输出为一个单独的二进制数据段{@link BinarySegmentHeader},
+ *
+ * 父的数据段中会将子数据段的内容合并输出到对应字段的位置;
+ *
+ *
+ * 在序列化输出数据段时,将按顺序先后输出类型编号({@link #code()})、版本标识、属性值列表;
+ * 其中,“版本标识”是根据类型编号和属性列表(由 {@link DataField} 顺序和类型决定,与名称无关) 进行 SHA256 哈希后映射到 64
+ * 位值空间的值,占用 8 字节空间;
+ * “版本标识” 用于在反序列化时校验数据格式的版本是否匹配,并允许数据契约升级后多版本数据并存;
+ *
+ * @author huanghaiquan
+ *
+ */
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DataContract {
+
+ /**
+ * 类型编号;
+ *
+ * 不同类型不能声明相同的编号;
+ *
+ *
+ * @return
+ */
+ int code();
+
+ String name() default "";
+
+ String description() default "";
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractEncoder.java
new file mode 100644
index 00000000..1cb0fc75
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractEncoder.java
@@ -0,0 +1,47 @@
+package com.jd.blockchain.binaryproto;
+
+import com.jd.blockchain.utils.io.BytesInputStream;
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlice;
+
+/**
+ * 二进制编码器;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface DataContractEncoder {
+
+ /**
+ * 数据契约的格式标准;
+ *
+ * @return
+ */
+ DataSpecification getSepcification();
+
+ /**
+ * 数据契约的接口类型;
+ *
+ * @return
+ */
+ Class> getContractType();
+
+ /**
+ * 按照数据格式标准序列化输出指定的数据对象;
+ *
+ * @param dataContract
+ * 数据对象;
+ * @param buffer
+ * 要写入的缓冲区;
+ * @return 返回写入的字节数;
+ */
+ int encode(Object dataContract, BytesOutputBuffer buffer);
+
+ /**
+ * 按照数据格式标准将指定的二进制输入流反序列化生成数据对象;
+ *
+ * @param bytesStream
+ * @return
+ */
+ T decode(BytesInputStream bytesStream);
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractException.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractException.java
new file mode 100644
index 00000000..ae7e57c3
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractException.java
@@ -0,0 +1,14 @@
+package com.jd.blockchain.binaryproto;
+
+public class DataContractException extends RuntimeException {
+
+ private static final long serialVersionUID = 5069307301932155810L;
+
+ public DataContractException(String message) {
+ super(message);
+ }
+ public DataContractException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractRegistry.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractRegistry.java
new file mode 100644
index 00000000..3c985b59
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractRegistry.java
@@ -0,0 +1,29 @@
+package com.jd.blockchain.binaryproto;
+
+import com.jd.blockchain.binaryproto.impl2.DataContractContext;
+
+/**
+ * 数据实体注册表;
+ *
+ * @author huanghaiquan
+ *
+ */
+public class DataContractRegistry {
+
+ private DataContractRegistry() {
+ }
+
+ public static DataContractEncoder register(Class> contractType) {
+ DataContractEncoder encoder = DataContractContext.resolve(contractType);
+ return encoder;
+ }
+
+ public static DataContractEncoder getEncoder(Class> contractType) {
+ return DataContractContext.ENCODER_LOOKUP.lookup(contractType);
+ }
+
+ public static DataContractEncoder getEncoder(int contractCode, long version) {
+ return DataContractContext.ENCODER_LOOKUP.lookup(contractCode, version);
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataField.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataField.java
new file mode 100644
index 00000000..fbd02711
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataField.java
@@ -0,0 +1,109 @@
+package com.jd.blockchain.binaryproto;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * 标记一个接口的字段作为数据契约的字段;
+ *
+ *
+ * 字段的数据类型需要需要显式通过
+ * {@link #primitiveType()}、{@link #refEnum()}、{@link #refContract()}
+ * 3个属性之一标注(只能标记一种);
+ *
+ * @author huanghaiquan
+ *
+ */
+@Target({ ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DataField {
+
+ /**
+ * 字段顺序;
+ *
+ *
+ * 顺序编号实际并不会输出,仅用于对字段进行升序排列;
+ *
+ *
+ * 注:对于已经发布使用的数据契约,都不应该调整顺序,否则会导致无法正确解析已有数据;
+ *
+ * @return
+ */
+ int order();
+
+ /**
+ * 基本数据类型;
+ *
+ *
+ * 如果字段的类型属于 {@link ValueType} 枚举中的基本数据类型,则需要显式指定一种具体的类型;
+ *
+ * @return
+ */
+ ValueType primitiveType() default ValueType.NIL;
+
+ /**
+ * 是否是枚举类型;
+ *
+ * 如果为 true,则属性的声明类型必须是枚举类型,且该枚举类型已经标记 {@link EnumContract};
+ *
+ * @return
+ */
+ boolean refEnum() default false;
+
+ /**
+ * 嵌套的数据契约类型;
+ *
+ * 如果为 true,则属性的声明类型必须是接口类型,且该类型已经标记了 {@link DataContract};
+ *
+ * @return
+ */
+ boolean refContract() default false;
+
+ /**
+ * 嵌套的契约类型是否根据实际的对象实现的契约接口动态写入;
+ *
+ * @return
+ */
+ boolean genericContract() default false;
+
+ /**
+ * 列表;
+ *
+ * @return
+ */
+ boolean list() default false;
+
+ /**
+ * 最大长度,单位为“byte”
+ *
+ * 仅对于文本、字节数组、大整数等相关的数据类型有效(即:{@link ValueType} 枚举中编码大于等于 0x20
+ * {@link ValueType#TEXT}的数据类型);
+ *
+ * @return
+ */
+ int maxSize() default -1;
+
+ /**
+ * 名称;
+ *
+ * 默认为属性的名称;
+ *
+ * @return
+ */
+ String name() default "";
+
+ /**
+ * 关于字段的说明;
+ *
+ *
+ * 说明内容将输出到数据段的数据结构描述文件;
+ *
+ * @return
+ */
+ String decription() default "";
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataSpecification.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataSpecification.java
new file mode 100644
index 00000000..9577d69e
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataSpecification.java
@@ -0,0 +1,65 @@
+package com.jd.blockchain.binaryproto;
+
+import java.util.List;
+
+/**
+ * {@link DataSpecification} 表示数据契约的格式标准;
+ *
+ * {@link DataSpecification} 提供了数据契约的格式描述(通过属性 {@link #getCode()}、
+ * {@link #getVersion()}、
+ * {@link #getFields()}),以及二进制数据片段序列的格式描述({@link #getSlices()});
+ *
+ *
+ * 其中,数据片段列表({@link #getSlices()})反映了数据契约实际输出的二进制序列:
+ * 1、首个数据片段是数据契约的类型编码({@link #getCode()});
+ * 2、接下来是数据契约的版本({@link #getVersion()});
+ * 3、再接下来是与字段列表({@link #getFields()})一一对应的数据分片;
+ *
+ *
+ *
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface DataSpecification {
+
+ /**
+ * 数据契约的类型编码;
+ *
+ * @return
+ */
+ int getCode();
+
+ /**
+ * 数据契约的版本;
+ *
+ *
+ * 由类型编码{@link #getCode()}和字段列表{@link #getFields()} 进行哈希生成的 64 位整数;
+ *
+ * @return
+ */
+ long getVersion();
+
+ String getName();
+
+ String getDescription();
+
+ /**
+ * 按定义顺序排列的字段格式标准的列表;
+ *
+ * 字段的顺序由 {@link DataField#order()} 定义;
+ *
+ * @return
+ */
+ List getFields();
+
+ /**
+ * 按顺序定义的二进制数据片段的格式标准的列表;
+ *
+ * @return
+ */
+ List getSlices();
+
+ String toHtml();
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumContract.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumContract.java
new file mode 100644
index 00000000..d8e8c452
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumContract.java
@@ -0,0 +1,26 @@
+package com.jd.blockchain.binaryproto;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EnumContract {
+
+ /**
+ * 类型编号;
+ *
+ *
+ * 而且,不同类型不能声明相同的编号;
+ *
+ * @return
+ */
+ int code() ;
+
+ String name() default "";
+
+ String decription() default "";
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumField.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumField.java
new file mode 100644
index 00000000..43074e98
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumField.java
@@ -0,0 +1,25 @@
+package com.jd.blockchain.binaryproto;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.jd.blockchain.utils.ValueType;
+
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EnumField {
+
+ /**
+ * 枚举值的类型;
+ *
+ *
+ * 注:只支持 {@link ValueType#INT8} ~ {@link ValueType#INT32} 这几种类型;
+ *
+ *
+ * @return
+ */
+ ValueType type();
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumSpecification.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumSpecification.java
new file mode 100644
index 00000000..c86f0bf0
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumSpecification.java
@@ -0,0 +1,24 @@
+package com.jd.blockchain.binaryproto;
+
+import java.util.Set;
+
+import com.jd.blockchain.utils.ValueType;
+
+public interface EnumSpecification {
+
+ int getCode();
+
+ String getName();
+
+ String getDescription();
+
+ long getVersion();
+
+ ValueType getValueType();
+
+ int[] getItemValues();
+
+ String[] getItemNames();
+
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/FieldSetter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/FieldSetter.java
new file mode 100644
index 00000000..1cea23ad
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/FieldSetter.java
@@ -0,0 +1,25 @@
+package com.jd.blockchain.binaryproto;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Created by zhangshuang3 on 2018/7/19.
+ */
+@Target({ ElementType.PARAMETER, ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface FieldSetter {
+
+ /**
+ * 名称;
+ *
+ * 默认为属性的名称;
+ *
+ * @return
+ */
+ String name() default "";
+ String type() default "";
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/FieldSpec.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/FieldSpec.java
new file mode 100644
index 00000000..1c3621d6
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/FieldSpec.java
@@ -0,0 +1,130 @@
+package com.jd.blockchain.binaryproto;
+
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * 表示数据契约字段的格式标准;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface FieldSpec {
+
+// /**
+// * 字段值的类型编码;
+// *
+// *
+// * 该编码是对{@link #getPrimitiveType()}、{@link #getRefEnum()}、{@link #getRefContract()}
+// * 3个属性的联合编码,通过首字节标识出类型为这3中类型中的一种;
+// * 其中:
+// * 1、首字节为 0 表示为基本类型 {@link #getPrimitiveType()};
+// * 2、首字节为 1 表示为枚举类型 {@link #getRefEnum()},紧接其后是枚举类型的编码
+// * 3、首字节为 2 表示为数据契约类型 {@link #getRefContract()};
+// * 4、首字节为 3 表示为基本类型的数组类型 {@link #isRepeatable()} ()};
+// * 5、首字节为 4 表示为PubKey类型 {@link #isRefPubKey()};
+// * 6、首字节为 5 表示为PrivKey类型 {@link #isRefPrivKey()};
+// * 7、首字节为 6 表示为HashDigest类型 {@link #isRefHashDigest()};
+// * 8、首字节为 7 表示为数据契约类型数组 {@link #isList(), @link #getRefContract()};
+// * 9、首字节为 8 表示为BlockChainIdentity数据类型 {@link #isRefIdentity()}
+// * 10、首字节为9 表示为NetworkAddress数据类型 {@link #isRefNetworkAddr()};
+// * @return
+// */
+// long getTypeCode();
+
+ /**
+ * 字段的值的类型;
+ *
+ * 如果不是字段的值不是基本类型,则返回 null(即: {@link DataField#primitiveType()} 设置为
+ * {@link ValueType#NIL});
+ *
+ * @return
+ */
+ ValueType getPrimitiveType();
+
+ /**
+ * 字段的值引用的枚举契约;
+ *
+ * 如果字段的值不是枚举契约类型,则返回 null;
+ *
+ * @return
+ */
+ EnumSpecification getRefEnum();
+
+ /**
+ * 字段的值引用的数据契约;
+ *
+ * 如果字段的值不是数据契约类型,则返回 null;
+ *
+ * @return
+ */
+ DataSpecification getRefContract();
+
+ boolean isRepeatable();
+
+// /**
+// * 字段的值引用的PubKey;
+// *
+// * 如果字段的值不是PubKey,则返回 false;
+// *
+// * @return
+// */
+// boolean isRefPubKey();
+// /**
+// * 字段的值引用的PrivKey;
+// *
+// * 如果字段的值不是PrivKey,则返回 false;
+// *
+// * @return
+// */
+// boolean isRefPrivKey();
+// /**
+// * 字段的值引用的HashDigest;
+// *
+// * 如果字段的值不是HashDigest,则返回 false;
+// *
+// * @return
+// */
+// boolean isRefHashDigest();
+// /**
+// * 字段的值引用的SignatureDigest;
+// *
+// * 如果字段的值不是SignatureDigest,则返回 false;
+// *
+// * @return
+// */
+// boolean isRefSignatureDigest();
+// /**
+// * 字段的值引用的BlockChainIdentity;
+// *
+// * 如果字段的值不是HashDigest,则返回 false;
+// *
+// * @return
+// */
+// boolean isRefIdentity();
+// /**
+// * 字段的值引用的NetworkAddress;
+// *
+// * 如果字段的值不是NetworkAddress,则返回 false;
+// *
+// * @return
+// */
+// boolean isRefNetworkAddr();
+ /**
+ * 最大长度;单位为“byte”;
+ *
+ * @return
+ * @see {@link DataField#maxSize()}
+ */
+ int getMaxSize();
+
+ String getName();
+
+ String getDescription();
+
+ /**
+ * 是否引用了一个通用数据契约类型,实际的类型需要根据实际的对象实例来定;
+ * @return
+ */
+ boolean isGenericContract();
+// Class> getContractTypeResolver();
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/BinaryEncoderImpl.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/BinaryEncoderImpl.java
new file mode 100644
index 00000000..c0c0f147
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/BinaryEncoderImpl.java
@@ -0,0 +1,861 @@
+//package com.jd.blockchain.binaryproto.impl;
+//
+//import my.utils.ValueType;
+//import my.utils.io.*;
+//import my.utils.net.NetworkAddress;
+//
+//import java.io.IOException;
+//import java.io.InputStream;
+//import java.io.OutputStream;
+//import java.lang.annotation.Annotation;
+//import java.lang.reflect.Constructor;
+//import java.lang.reflect.Field;
+//import java.lang.reflect.InvocationTargetException;
+//import java.lang.reflect.Method;
+//import java.util.*;
+//
+//import com.jd.blockchain.binaryproto.BinaryEncoder;
+//import com.jd.blockchain.binaryproto.ContractTypeResolver;
+//import com.jd.blockchain.binaryproto.DConstructor;
+//import com.jd.blockchain.binaryproto.DataContractException;
+//import com.jd.blockchain.binaryproto.DataContractRegistry;
+//import com.jd.blockchain.binaryproto.DataSpecification;
+//import com.jd.blockchain.binaryproto.EnumSpecification;
+//import com.jd.blockchain.binaryproto.FieldSetter;
+//import com.jd.blockchain.binaryproto.FieldSpec;
+//
+///**
+// * Created by zhangshuang3 on 2018/6/21.
+// */
+//public class BinaryEncoderImpl implements BinaryEncoder {
+// private DataSpecification spec;
+// private Class> contractType;
+//
+// public BinaryEncoderImpl(DataSpecification spec, Class> contractType) {
+// this.spec = spec;
+// this.contractType = contractType;
+// }
+//
+// @Override
+// public DataSpecification getSepcification() {
+// return spec;
+// }
+//
+// @Override
+// public Class> getContractType() {
+// return contractType;
+// }
+//
+// public void write(FieldSpec spec, OutputStream out, Object value) {
+// ValueType primitive = spec.getPrimitiveType();
+// EnumSpecification refEnum = spec.getRefEnum();
+// DataSpecification refContract = spec.getRefContract();
+// boolean list = spec.isRepeatable();
+// boolean refPubKey = spec.isRefPubKey();
+// boolean refPrivKey = spec.isRefPrivKey();
+// boolean refHashDigest = spec.isRefHashDigest();
+// boolean refSignatureDigest = spec.isRefSignatureDigest();
+// boolean refIdentity = spec.isRefIdentity();
+// boolean refNetworkAddr = spec.isRefNetworkAddr();
+// Class> contractTypeResolverClass = spec.getContractTypeResolver();
+//
+// try {
+// // basic data type
+// if ((primitive != ValueType.NIL) && (list == false)) {
+// writePrimitive(value, primitive, out);
+// }
+// // enum type
+// else if (refEnum != null) {
+// writeEnum(value, refEnum, out);
+// }
+// // ref contract
+// else if (refContract != null && list == false) {
+// writeContract(value, refContract, contractTypeResolverClass, out);
+// }
+// // array type
+// else if ((primitive != ValueType.NIL) && (list == true)) {
+// writePrimitiveList(value, primitive, out);
+// }
+// // refcontract array
+// else if (refContract != null && list == true) {
+// writeContractList(value, refContract, contractTypeResolverClass, out);
+// }
+// // HashDigest type | PubKey type | PrivKey type
+// else if (refPubKey == true | refPrivKey == true | refHashDigest == true | refSignatureDigest == true) {
+// writeBytesSerializeObject(value, out);
+// }
+// // BlockChainIdentity type
+// // else if (refIdentity == true) {
+// // if (obj == null) {
+// // BytesEncoding.writeInNormal(null, out);
+// // }
+// // else {
+// // BytesEncoding.writeInNormal(obj.getClass().getName().getBytes(), out);
+// // BytesWriter writer = (BytesWriter)obj;
+// // writer.writeTo(out);
+// // }
+// // }
+// // NetworkAddress type
+// else if (refNetworkAddr == true) {
+// wirteNetworkAddressValue(value, out);
+// } else {
+// throw new IllegalStateException("Unexpected contract field and value!");
+// }
+// } catch (InstantiationException | IllegalAccessException e) {
+// throw new DataContractException(e.getMessage(), e);
+// }
+//
+// }
+//
+// private void wirteNetworkAddressValue(Object value, OutputStream out) {
+// if (value == null) {
+// BytesEncoding.writeInNormal(null, out);
+// } else {
+// // write host ,port and secure flag
+// NetworkAddress address = (NetworkAddress) value;
+// BytesEncoding.writeInNormal(address.getHost().getBytes(), out);
+// BytesUtils.writeInt(address.getPort(), out);
+// BytesUtils.writeByte((byte) (address.isSecure() ? 1 : 0), out);
+// }
+// }
+//
+// private void writeBytesSerializeObject(Object value, OutputStream out) {
+// if (value == null) {
+// BytesEncoding.writeInNormal(null, out);
+// } else {
+// BytesEncoding.writeInNormal(value.getClass().getName().getBytes(), out);
+// BytesSerializable serializable = (BytesSerializable) value;
+// // refPubKey:1bytes algorithm code , 1byte key type, and others are raw bytes
+// // refPrivKey:1bytes algorithm code , 1byte key type, and others are raw bytes
+// // refHashDigest:1bytes algorithm code , and others are raw bytes
+// BytesEncoding.writeInNormal(serializable.toBytes(), out);
+// }
+// }
+//
+// private void writeContractList(Object value, DataSpecification contractSpeci, Class> contractTypeResolverClass,
+// OutputStream out) throws InstantiationException, IllegalAccessException {
+// BinaryEncoder encoder1 = null;
+// Object[] refContractArray = (Object[]) value;
+// if (refContractArray == null) {
+// BytesUtils.writeInt(0, out);
+// } else {
+// BytesUtils.writeInt(refContractArray.length, out);
+// if (contractTypeResolverClass.isInterface() == true) {
+// encoder1 = DataContractRegistry.getEncoder(contractSpeci.getCode(), contractSpeci.getVersion());
+// if (encoder1 == null) {
+// throw new DataContractException("write: get encoder null error!");
+// }
+// for (Object ref : refContractArray) {
+// BytesUtils.writeInt(encoder1.getSepcification().getCode(), out);
+// BytesUtils.writeLong(encoder1.getSepcification().getVersion(), out);
+// // record class name
+// BytesEncoding.writeInNormal(ref.getClass().getName().getBytes(), out);
+// encoder1.encode(ref, out);
+// }
+// } else {
+// // TODO: 不必每次都实例化,应该对此实例建立单例缓存;
+// ContractTypeResolver resolver = (ContractTypeResolver) (contractTypeResolverClass.newInstance());
+// for (Object ref : refContractArray) {
+// Class> subContractType = resolver.getContractType(ref, null);
+// encoder1 = DataContractRegistry.register(subContractType);
+// if (encoder1 == null) {
+// throw new DataContractException("write: regist sub contract type failed error!");
+// }
+// BytesUtils.writeInt(encoder1.getSepcification().getCode(), out);
+// BytesUtils.writeLong(encoder1.getSepcification().getVersion(), out);
+// // record class name
+// BytesEncoding.writeInNormal(ref.getClass().getName().getBytes(), out);
+// encoder1.encode(ref, out);
+// encoder1 = null;
+// }
+// }
+// }
+// }
+//
+// private void writePrimitiveList(Object value, ValueType primitive, OutputStream out) {
+// switch (primitive) {
+// case BOOLEAN:
+// if (value == null) {
+// BytesUtils.writeInt(0, out);
+// break;
+// }
+//
+// boolean[] boolArray = (boolean[]) value;
+// BytesUtils.writeInt(boolArray.length, out);
+// for (boolean i : boolArray) {
+// BytesUtils.writeByte((byte) (i ? 1 : 0), out);
+// }
+// break;
+// case INT8:
+// if (value == null) {
+// BytesUtils.writeInt(0, out);
+// break;
+// }
+// byte[] byteArray = (byte[]) value;
+// BytesUtils.writeInt(byteArray.length, out);
+// for (byte i : byteArray) {
+// BytesUtils.writeByte(i, out);
+// }
+// break;
+// case INT16:
+// if (value == null) {
+// BytesUtils.writeInt(0, out);
+// break;
+// }
+// short[] shortArray = (short[]) value;
+// BytesUtils.writeInt(shortArray.length, out);
+// for (short i : shortArray) {
+// byte[] bytes = BytesUtils.toBytes(i);
+// BytesEncoding.writeInShort(bytes, out);
+// }
+// break;
+// case INT32:
+// if (value == null) {
+// BytesUtils.writeInt(0, out);
+// break;
+// }
+// int[] intArray = (int[]) value;
+// BytesUtils.writeInt(intArray.length, out);
+// for (int i : intArray) {
+// BytesUtils.writeInt(i, out);
+// }
+// break;
+// case INT64:
+// if (value == null) {
+// BytesUtils.writeInt(0, out);
+// break;
+// }
+// long[] longArray = (long[]) value;
+// BytesUtils.writeInt(longArray.length, out);
+// for (long i : longArray) {
+// BytesUtils.writeLong(i, out);
+// }
+// break;
+// case DATETIME:
+// if (value == null) {
+// BytesUtils.writeInt(0, out);
+// break;
+// }
+// Date[] dateArray = (Date[]) value;
+// BytesUtils.writeInt(dateArray.length, out);
+// long elemSeconds;
+// for (Date i : dateArray) {
+// elemSeconds = i.getTime();
+// BytesUtils.writeLong(elemSeconds, out);
+// }
+// break;
+// case BYTES:
+// if (value == null) {
+// BytesUtils.writeInt(0, out);
+// break;
+// }
+// ByteArray[] byteArrays = (ByteArray[]) value;
+// BytesUtils.writeInt(byteArrays.length, out);
+// for (ByteArray elem : byteArrays) {
+// BytesEncoding.writeInNormal(elem.bytes(), out);
+// }
+// break;
+// case TEXT:
+// case JSON:
+// case XML:
+// case BIG_INT:
+// case IMG:
+// case VIDEO:
+// case LOCATION:
+// if (value == null) {
+// BytesUtils.writeInt(0, out);
+// break;
+// }
+// Object[] dynamicArray = (Object[]) value;
+// BytesUtils.writeInt(dynamicArray.length, out);
+// for (Object i : dynamicArray) {
+// BytesEncoding.writeInNormal(i.toString().getBytes(), out);
+// }
+// break;
+// default:
+// throw new DataContractException("write: array type error!");
+// }
+// }
+//
+// private void writeContract(Object value, DataSpecification contractSpeci, Class> contractTypeResolverClass,
+// OutputStream out) throws InstantiationException, IllegalAccessException {
+// BinaryEncoder encoder = null;
+// if (contractTypeResolverClass.isInterface() == true) {
+// encoder = DataContractRegistry.getEncoder(contractSpeci.getCode(), contractSpeci.getVersion());
+// if (encoder == null) {
+// throw new DataContractException("write: get encoder null error!");
+// }
+// } else {
+// // get sub contract type
+// ContractTypeResolver resolver = (ContractTypeResolver) (contractTypeResolverClass.newInstance());
+// if (resolver == null) {
+// throw new DataContractException("write: newInstance null error!");
+// }
+// Class> subContractType = resolver.getContractType(value, null);
+// encoder = DataContractRegistry.register(subContractType);
+// if (encoder == null) {
+// throw new DataContractException("write: regist sub contract type failed error!");
+// }
+// }
+// BytesUtils.writeInt(encoder.getSepcification().getCode(), out);
+// BytesUtils.writeLong(encoder.getSepcification().getVersion(), out);
+// if (value == null) {
+// BytesEncoding.writeInNormal(null, out);
+// } else {
+// // record class name
+// BytesEncoding.writeInNormal(value.getClass().getName().getBytes(), out);
+// encoder.encode(value, out);
+// }
+// }
+//
+// private void writeEnum(Object value, EnumSpecification enumType, OutputStream out) {
+// int code = 0;
+// EnumSpecificationImpl refEnumImpl = (EnumSpecificationImpl) enumType;
+// Map constant = refEnumImpl.getEnumConstants();
+// for (Object enumConstant : constant.keySet()) {
+// if (enumConstant.toString().equals(value.toString()) == true) {
+// code = constant.get(enumConstant);
+// break;
+// }
+// }
+// switch (refEnumImpl.getValueType()) {
+// case INT8:
+// BytesUtils.writeByte((byte) (code & 0xff), out);
+// break;
+// case INT16:
+// byte[] bytes = BytesUtils.toBytes((short) code);
+// BytesEncoding.writeInShort(bytes, out);
+// break;
+// case INT32:
+// BytesUtils.writeInt(code, out);
+// break;
+// default:
+// throw new DataContractException("write: enum type error!");
+// }
+// }
+//
+// private void writePrimitive(Object value, ValueType primitive, OutputStream out) {
+// switch (primitive) {
+// case BOOLEAN:
+// BytesUtils.writeByte((byte) ((boolean) value ? 1 : 0), out);
+// break;
+// case INT8:
+// BytesUtils.writeByte((byte) value, out);
+// break;
+// case INT16:
+// // TODO 可修改为short类型
+// byte[] bytes = BytesUtils.toBytes((short) value);
+// BytesEncoding.writeInShort(bytes, out);
+// break;
+// case INT32:
+// BytesUtils.writeInt((int) value, out);
+// break;
+// case INT64:
+// BytesUtils.writeLong((long) value, out);
+// break;
+// case DATETIME:
+// long seconds = ((Date) value).getTime();
+// BytesUtils.writeLong(seconds, out);
+// break;
+// case BYTES:
+// ByteArray byteArray = (ByteArray) value;
+// BytesEncoding.writeInNormal(byteArray.bytes(), out);
+// break;
+// case TEXT:
+// case JSON:
+// case XML:
+// case BIG_INT:
+// case IMG:
+// case VIDEO:
+// case LOCATION:
+// if (value == null) {
+// BytesEncoding.writeInNormal(null, out);
+// } else {
+// BytesEncoding.writeInNormal(value.toString().getBytes(), out);
+// }
+// break;
+// default:
+// throw new DataContractException("write: primitive type error!");
+// }
+// }
+//
+// @Override
+// public void encode(Object data, OutputStream out) {
+// DataSpecificationImpl impl = (DataSpecificationImpl) this.getSepcification();
+// List fields = impl.getFields();
+//
+// try {
+// for (FieldSpec spec : fields) {
+// Method mth = ((FieldSpecImpl) spec).getReadMethod();
+// // mth.setAccessible(true);
+// Object obj = mth.invoke(data);
+// write(spec, out, obj);
+// }
+// } catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+// throw new DataContractException(e.getMessage(), e);
+// }
+// }
+//
+// public void read(FieldSpec spec, Method mth, InputStream in, Map fieldOutter) {
+// ValueType primitive = spec.getPrimitiveType();
+// EnumSpecification refEnum = spec.getRefEnum();
+// DataSpecification refContract = spec.getRefContract();
+// boolean list = spec.isRepeatable();
+// boolean refPubKey = spec.isRefPubKey();
+// boolean refPrivKey = spec.isRefPrivKey();
+// boolean refHashDigest = spec.isRefHashDigest();
+// boolean refSignatureDigest = spec.isRefSignatureDigest();
+// boolean refIdentity = spec.isRefIdentity();
+// boolean refNetworkAddr = spec.isRefNetworkAddr();
+//
+// try {
+// // primitive data type
+// if ((primitive != ValueType.NIL) && (list == false)) {
+// switch (primitive) {
+// case BOOLEAN:
+// boolean boolValue = BytesUtils.readByte(in) == 1 ? true : false;
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), boolValue);
+// }
+// break;
+// case INT8:
+// byte int8Value = BytesUtils.readByte(in);
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), int8Value);
+// }
+// break;
+// case INT16:
+// short shortValue = (short) ((BytesUtils.readByte(in) << 8) | (BytesUtils.readByte(in)));
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), shortValue);
+// }
+// break;
+// case INT32:
+// int intValue = BytesUtils.readInt(in);
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), intValue);
+// }
+// break;
+// case INT64:
+// long value = BytesUtils.readLong(in);
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), value);
+// }
+// break;
+// case DATETIME:
+// long seconds = BytesUtils.readLong(in);
+// Date date = new Date(seconds);
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), date);
+// }
+// break;
+// case BYTES:
+// byte[] bytes = BytesEncoding.read(NumberMask.NORMAL, in);
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), ByteArray.wrap(bytes));
+// }
+// break;
+// case TEXT:
+// case JSON:
+// case XML:
+// case BIG_INT:
+// case IMG:
+// case VIDEO:
+// case LOCATION:
+// byte[] dynamicResult = BytesEncoding.read(NumberMask.NORMAL, in);
+// if (dynamicResult.length == 0) {
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), null);
+// }
+// } else {
+// StringBuffer buffer = new StringBuffer();
+// for (byte i : dynamicResult) {
+// buffer.append((char) i);
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), buffer.toString());
+// }
+// }
+// break;
+// default:
+// throw new DataContractException("read: primitive type error!");
+// }
+// }
+// // enum type
+// else if (refEnum != null) {
+// int code = 0;
+// switch (refEnum.getValueType()) {
+// case INT8:
+// code = BytesUtils.readByte(in);
+// break;
+// case INT16:
+// code = (BytesUtils.readByte(in) << 8) | (BytesUtils.readByte(in));
+// break;
+// case INT32:
+// code = BytesUtils.readInt(in);
+// break;
+// default:
+// throw new DataContractException("read: enum type error!");
+// }
+// EnumSpecificationImpl refEnumImpl = (EnumSpecificationImpl) refEnum;
+// Map constant = refEnumImpl.getEnumConstants();
+// Object enumConstant = null;
+// for (Map.Entry vo : constant.entrySet()) {
+// if (vo.getValue() == code) {
+// enumConstant = vo.getKey();
+// break;
+// }
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), enumConstant);
+// }
+// }
+// // ref contract type
+// else if (refContract != null && list == false) {
+// BinaryEncoder encoder = null;
+// Object object = null;
+// int code = BytesUtils.readInt(in);
+// long version = BytesUtils.readLong(in);
+// encoder = DataContractRegistry.getEncoder(code, version);
+// if (encoder == null) {
+// throw new DataContractException("read: get encoder null error!");
+// }
+// byte[] className = BytesEncoding.read(NumberMask.NORMAL, in);
+// if (className.length != 0) {
+// StringBuffer buffer = new StringBuffer();
+// for (byte i : className) {
+// buffer.append((char) i);
+// }
+// object = encoder.decode(in, null, Class.forName(buffer.toString()));
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), object);
+// }
+// }
+// // array type
+// else if ((primitive != ValueType.NIL) && list == true) {
+// int arrayCount = BytesUtils.readInt(in);
+// int i;
+//
+// if (arrayCount == 0) {
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), null);
+// }
+// } else {
+// switch (primitive) {
+// case BOOLEAN:
+// boolean[] boolArray = new boolean[arrayCount];
+// for (i = 0; i < arrayCount; i++) {
+// boolArray[i] = (BytesUtils.readByte(in) == 1) ? true : false;
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), boolArray);
+// }
+// break;
+// case INT8:
+// byte[] byteArray = new byte[arrayCount];
+// for (i = 0; i < arrayCount; i++) {
+// byteArray[i] = (BytesUtils.readByte(in));
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), byteArray);
+// }
+// break;
+// case INT16:
+// short[] shortArray = new short[arrayCount];
+// for (i = 0; i < arrayCount; i++) {
+// shortArray[i] = (short) ((BytesUtils.readByte(in) << 8) | (BytesUtils.readByte(in)));
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), shortArray);
+// }
+// break;
+// case INT32:
+// int[] intArray = new int[arrayCount];
+// for (i = 0; i < arrayCount; i++) {
+// intArray[i] = BytesUtils.readInt(in);
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), intArray);
+// }
+// break;
+// case INT64:
+// long[] longArray = new long[arrayCount];
+// for (i = 0; i < arrayCount; i++) {
+// longArray[i] = BytesUtils.readLong(in);
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), longArray);
+// }
+// break;
+// case DATETIME:
+// Date[] dateArray = new Date[arrayCount];
+// for (i = 0; i < arrayCount; i++) {
+// long seconds = BytesUtils.readLong(in);
+// dateArray[i].setTime(seconds);
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), dateArray);
+// }
+// break;
+// case TEXT:
+// case JSON:
+// case XML:
+// case BYTES:
+// case BIG_INT:
+// case IMG:
+// case VIDEO:
+// case LOCATION:
+// StringBuffer[] stringBufferArray = new StringBuffer[arrayCount];
+// String[] buffer = new String[arrayCount];
+// for (i = 0; i < arrayCount; i++) {
+// byte[] dynamicResult = BytesEncoding.read(NumberMask.NORMAL, in);
+// stringBufferArray[i] = new StringBuffer();
+// for (byte j : dynamicResult) {
+// stringBufferArray[i].append((char) j);
+// }
+// buffer[i] = stringBufferArray[i].toString();
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), buffer);
+// }
+// break;
+// default:
+// throw new DataContractException("read: array type error!");
+// }
+// }
+// }
+// // ref contract type array
+// else if (refContract != null && list == true) {
+// int code;
+// long version;
+// BinaryEncoder encoder = null;
+// Object[] refContractArray = null;
+// int refContractArraySize = BytesUtils.readInt(in);
+// if (refContractArraySize != 0) {
+// refContractArray = new Object[refContractArraySize];
+// for (int i = 0; i < refContractArray.length; i++) {
+// code = BytesUtils.readInt(in);
+// version = BytesUtils.readLong(in);
+// encoder = DataContractRegistry.getEncoder(code, version);
+// if (encoder == null) {
+// throw new DataContractException("read: get encoder null error!");
+// }
+// byte[] className = BytesEncoding.read(NumberMask.NORMAL, in);
+// if (className.length == 0) {
+// refContractArray[i] = null;
+// } else {
+// StringBuffer buffer = new StringBuffer();
+// for (byte var : className) {
+// buffer.append((char) var);
+// }
+// refContractArray[i] = encoder.decode(in, null, Class.forName(buffer.toString()));
+// }
+// }
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), refContractArray);
+// }
+// } else if (refPubKey == true | refPrivKey == true | refHashDigest == true | refSignatureDigest == true) {
+// Object object = null;
+// byte[] className = BytesEncoding.read(NumberMask.NORMAL, in);
+//
+// if (className.length != 0) {
+// StringBuffer buffer = new StringBuffer();
+// for (byte var : className) {
+// buffer.append((char) var);
+// }
+// byte[] bytes = BytesEncoding.read(NumberMask.NORMAL, in);
+// object = Class.forName(buffer.toString()).getConstructor(byte[].class).newInstance(bytes);
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), object);
+// }
+// }
+// // else if (refIdentity == true) {
+// // BytesReader reader = null;
+// // byte[] className = BytesEncoding.read(NumberMask.NORMAL, in);
+// // if (className.length != 0) {
+// // StringBuffer buffer = new StringBuffer();
+// // for (byte var : className) {
+// // buffer.append((char) var);
+// // }
+// // reader = (BytesReader)Class.forName(buffer.toString()).newInstance();
+// // reader.resolvFrom(in);
+// // }
+// // if (fieldOutter != null) {
+// // fieldOutter.put(mth.getName(), reader);
+// // }
+// // }
+// else if (refNetworkAddr == true) {
+// NetworkAddress networkAddress = null;
+// byte[] buffer = BytesEncoding.read(NumberMask.NORMAL, in);
+// if (buffer.length != 0) {
+// StringBuffer host = new StringBuffer();
+// for (byte var : buffer) {
+// host.append((char) var);
+// }
+// int port = BytesUtils.readInt(in);
+// boolean secure = BytesUtils.readByte(in) == 1 ? true : false;
+// networkAddress = new NetworkAddress(host.toString(), port, secure);
+// }
+// if (fieldOutter != null) {
+// fieldOutter.put(mth.getName(), networkAddress);
+// }
+// }
+// } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException
+// | InvocationTargetException e) {
+// throw new DataContractException(e.getMessage(), e);
+// }
+// }
+//
+// public boolean checkConstructor(Class> concretedDataType) {
+// int count = 0;
+// Constructor>[] constructors = concretedDataType.getConstructors();
+// for (Constructor constructor : constructors) {
+// if (constructor.getDeclaredAnnotation(DConstructor.class) != null) {
+// count++;
+// }
+// }
+// if (count >= 2) {
+// return false;
+// }
+// return true;
+// }
+//
+// // convert the first char to lowercase
+// public String toLowerCaseFirstOne(String s) {
+// if (Character.isLowerCase(s.charAt(0)))
+// return s;
+// else
+// return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
+// }
+//
+// public void executeSet(List fields, Map fieldOutter, Object object,
+// Class> concretedDataType) {
+// Method mth;
+// try {
+// for (FieldSpec spec : fields) {
+// mth = ((FieldSpecImpl) spec).getReadMethod();
+// if (mth.getName().length() < 4) {
+// throw new DataContractException("executeSet: mth name error!");
+// }
+// // skip "get",get substring
+// String getName = mth.getName().substring(3);
+// // "set" concat with getmthName
+// String setName = "set".concat(getName);
+// Method[] allMths = concretedDataType.getMethods();
+// Method setMth = null;
+// for (Method x : allMths) {
+// if (x.getName().equals(setName)) {
+// setMth = x;
+// break;
+// }
+// }
+// if (setMth != null) {
+// // invoke related set method
+// Object arg = fieldOutter.get(mth.getName());
+// if (arg != null) {
+// setMth.invoke(object, arg);
+// }
+// }
+// // set related member field
+// else {
+// String member = toLowerCaseFirstOne(getName);
+// Field field = concretedDataType.getDeclaredField(member);
+// if (field != null) {
+// field.setAccessible(true);
+// field.set(object, fieldOutter.get(mth.getName()));
+// }
+// }
+// }
+// } catch (IllegalAccessException | InvocationTargetException | NoSuchFieldException e) {
+// throw new DataContractException(e.getMessage(), e);
+// }
+// }
+//
+// @Override
+// public Object decode(InputStream in, Map fieldOutter, Class> concretedDataType) {
+// // TODO: 未缓存对实现类的解析;
+// DataSpecificationImpl impl = (DataSpecificationImpl) this.getSepcification();
+// List fields = impl.getFields();
+// Constructor constructor = null;
+// Object object = null;
+// Method mthType = null;
+//
+// if (fieldOutter == null) {
+// fieldOutter = new HashMap<>();
+// }
+//
+// try {
+// // first check constructor with annotation, count >=2 throw exception
+// if (checkConstructor(concretedDataType) == false) {
+// throw new DataContractException("decode: constructor with annotation number error!");
+// }
+// // get constructor with annotation
+// for (Constructor construct : concretedDataType.getConstructors()) {
+// if (construct.getDeclaredAnnotation(DConstructor.class) != null) {
+// constructor = construct;
+// break;
+// }
+// }
+// // fill fieldOutter with fieldsetter
+// for (FieldSpec spec : fields) {
+// Method mth = ((FieldSpecImpl) spec).getReadMethod();
+// if (mth == null) {
+// throw new DataContractException("decode: mth null error!");
+// }
+// read(spec, mth, in, fieldOutter);
+// }
+// // save constructor parameters
+// if (constructor != null) {
+// Annotation[][] annotations = constructor.getParameterAnnotations();
+// Object[] obj = new Object[annotations.length];
+// int i = 0;
+// for (Annotation[] annoArray : annotations) {
+// for (Annotation annotation : annoArray) {
+// FieldSetter anno = (FieldSetter) annotation;
+// obj[i] = fieldOutter.get(anno.name());
+// for (FieldSpec spec : fields) {
+// mthType = ((FieldSpecImpl) spec).getReadMethod();
+// // in case :constructor and data contract method name is same ,but return type
+// // is different
+// if (mthType.getName().equals(anno.name())) {
+// String retType = mthType.getReturnType().getSimpleName();
+// String annoType = anno.type();
+// if ((retType.equals(annoType) == false) && (retType.equals("ByteArray"))
+// && (annoType.equals("byte[]"))) {
+// ByteArray byteArray = (ByteArray) obj[i];
+// obj[i] = byteArray.bytes();
+// break;
+// } else if ((retType.equals(annoType) == false) && (retType.equals("byte[]"))
+// && (annoType.equals("ByteArray"))) {
+// byte[] bytes = (byte[]) obj[i];
+// obj[i] = ByteArray.wrap(bytes);
+// break;
+// }
+// }
+// }
+// i++;
+// }
+// }
+// // exec constructor with parameters
+// object = constructor.newInstance(obj);
+// }
+// if (object == null) {
+// // use default constructor,
+// constructor = concretedDataType.getDeclaredConstructor();
+// if (!constructor.isAccessible()) {
+// constructor.setAccessible(true);
+// }
+// object = constructor.newInstance();
+// }
+// // exec set method
+// executeSet(fields, fieldOutter, object, concretedDataType);
+// return object;
+// } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException
+// | SecurityException e) {
+// throw new DataContractException(e.getMessage(), e);
+// }
+// }
+//}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/DataSpecificationImpl.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/DataSpecificationImpl.java
new file mode 100644
index 00000000..4276c83c
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/DataSpecificationImpl.java
@@ -0,0 +1,94 @@
+//package com.jd.blockchain.binaryproto.impl;
+//
+//import java.lang.reflect.Method;
+//import java.util.*;
+//
+//import com.jd.blockchain.binaryproto.BinarySliceSpec;
+//import com.jd.blockchain.binaryproto.DataField;
+//import com.jd.blockchain.binaryproto.DataSpecification;
+//import com.jd.blockchain.binaryproto.FieldSpec;
+//
+///**
+// * Created by zhangshuang3 on 2018/6/21.
+// */
+//public class DataSpecificationImpl implements DataSpecification {
+// private int code;
+// private long version;
+// private String name;
+// private String description;
+// private List fieldList = new ArrayList();
+// private List sliceList = new ArrayList<>();
+//
+//
+// public DataSpecificationImpl() {
+//
+// }
+// //sort method by order id
+// public Map sortMapByValues (Map mths) {
+// Set> mapEntries = mths.entrySet();
+// List> aList = new LinkedList>(mapEntries);
+// //sort list
+// Collections.sort(aList, new Comparator>() {
+// @Override
+// public int compare(Map.Entry ele1,
+// Map.Entry ele2) {
+// return (ele1.getValue().getAnnotation(DataField.class).order()) - (ele2.getValue().getAnnotation(DataField.class).order());
+// }
+// });
+// int count = aList.size();
+// //init Capacity
+// Map aMap = new LinkedHashMap(count);
+// for(Map.Entry entry: aList) {
+// aMap.put(entry.getKey(), entry.getValue());
+// }
+// return aMap;
+// }
+//
+// @Override
+// public int getCode() {
+// return code;
+// }
+// public void setCode(int code) {
+// this.code = code;
+// }
+//
+// @Override
+// public long getVersion() {
+// return version;
+// }
+// public void setVersion(long version) {
+// this.version = version;
+// }
+//
+// @Override
+// public String getName() {
+// return name;
+// }
+// public void setName(String name) {
+// this.name = name;
+// }
+//
+// @Override
+// public String getDescription() {
+// return description;
+// }
+// public void setDescription(String description) {this.description = description;}
+//
+// @Override
+// public List getFields() {
+// return fieldList;
+// }
+// public void setFields(FieldSpec field) {this.fieldList.add(field);}
+//
+// @Override
+// public List getSlices() {
+// return sliceList;
+// }
+// public void setSlices(BinarySliceSpec slice) {
+// this.sliceList.add(slice);
+// }
+// @Override
+// public String toHtml() {
+// return null;
+// }
+//}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/EnumContractRegistry.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/EnumContractRegistry.java
new file mode 100644
index 00000000..7ac27dee
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/EnumContractRegistry.java
@@ -0,0 +1,32 @@
+package com.jd.blockchain.binaryproto.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.jd.blockchain.binaryproto.EnumSpecification;
+
+/**
+ * Created by zhangshuang3 on 2018/6/27.
+ */
+public class EnumContractRegistry {
+ private static Map enumSpecs = new HashMap();
+
+ public EnumContractRegistry() {
+ }
+
+ public static EnumSpecification getEnumSpec(Class> contractType) {
+ //find encoder from dataSpecs by contractType
+ for (String key : enumSpecs.keySet())
+ {
+ if (key.equals(contractType.getName())) {
+ return enumSpecs.get(key);
+ }
+ }
+ return null;
+ }
+
+ public static void setEnumSpecs(String key, EnumSpecification value) {
+ enumSpecs.put(key, value);
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/EnumSpecificationImpl.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/EnumSpecificationImpl.java
new file mode 100644
index 00000000..5a500802
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/EnumSpecificationImpl.java
@@ -0,0 +1,84 @@
+//package com.jd.blockchain.binaryproto.impl;
+//
+//import java.lang.reflect.Method;
+//import java.util.*;
+//
+//import com.jd.blockchain.binaryproto.EnumSpecification;
+//
+//import my.utils.ValueType;
+//
+///**
+// * Created by zhangshuang3 on 2018/6/21.
+// */
+//public class EnumSpecificationImpl implements EnumSpecification {
+// private int code;
+// private long version;
+// private String name;
+// private String description;
+// private ValueType item;
+// private Set intSet = new LinkedHashSet<>();
+// private Set stringSet = new LinkedHashSet<>();
+// private Map readEnumConstants = new HashMap();
+//
+// public EnumSpecificationImpl(int code, String name, String description) {
+// this.code = code;
+// this.name = name;
+// this.description = description;
+// }
+// @Override
+// public int getCode() {
+// return this.code;
+// }
+// public void setCode(int code) {
+// this.code = code;
+// }
+// @Override
+// public long getVersion(){
+// return this.version;
+// }
+// public void setVersion(long version) {
+// this.version = version;
+// }
+// @Override
+// public String getName() {
+// return this.name;
+// }
+// public void setName(String name) {
+// this.name = name;
+// }
+// @Override
+// public String getDescription() {
+// return this.description;
+// }
+// public void setDescription(String description) {
+// this.description = description;
+// }
+// @Override
+// public ValueType getValueType() {
+// return this.item;
+// }
+// public void setItemType(ValueType item) {
+// this.item = item;
+// }
+// @Override
+// public Set getItemValues() {
+// return this.intSet;
+// }
+// public void setItemValues(Integer item) {
+// this.intSet.add(item);
+// }
+// @Override
+// public Set getItemNames() {
+// return this.stringSet;
+// }
+// public void setItemNames(String item) {
+// this.stringSet.add(item);
+// }
+//
+// public Map getEnumConstants() {
+// return this.readEnumConstants;
+// }
+// public void setEnumConstants(Object enumConstant , Integer code) {
+// this.readEnumConstants.put(enumConstant, code);
+// }
+//}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/FieldSpecImpl.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/FieldSpecImpl.java
new file mode 100644
index 00000000..fd47d667
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/FieldSpecImpl.java
@@ -0,0 +1,142 @@
+//package com.jd.blockchain.binaryproto.impl;
+//
+//
+//import org.omg.CORBA.PUBLIC_MEMBER;
+//
+//import com.jd.blockchain.binaryproto.DataSpecification;
+//import com.jd.blockchain.binaryproto.EnumSpecification;
+//import com.jd.blockchain.binaryproto.FieldSpec;
+//
+//import my.utils.ValueType;
+//
+//import java.lang.reflect.Method;
+//
+///**
+// * Created by zhangshuang3 on 2018/6/21.
+// */
+//public class FieldSpecImpl implements FieldSpec {
+// private int typeCode;
+// private ValueType primitiveType;
+// private EnumSpecification enumSpec;
+// private DataSpecification dataSpec;
+// private boolean isRefPubKey;
+// private boolean isRefPrivKey;
+// private boolean isRefHashDigest;
+// private boolean isRefSignatureDigest;
+// private boolean isRefIdentity;
+// private boolean isRefNetworkAddr;
+// private String name;
+// private String description;
+// private boolean isList;
+// private int maxLength;
+// private Class> contractTypeResolver;
+// private Method readMethod;
+//
+// public FieldSpecImpl() {
+//
+// }
+// public Method getReadMethod() {
+// return readMethod;
+// }
+// public void setReadMethod(Method readMethod) {
+// this.readMethod = readMethod;
+// readMethod.setAccessible(true);
+// }
+// @Override
+// public int getTypeCode() {return typeCode;}
+// public void setTypeCode(int typeCode) {this.typeCode = typeCode;}
+//
+// @Override
+// public ValueType getPrimitiveType() {return primitiveType;}
+// public void setPrimitiveType(ValueType primitiveType) {
+// this.primitiveType = primitiveType;
+// }
+// @Override
+// public EnumSpecification getRefEnum() {return enumSpec;}
+// public void setRefEnum(EnumSpecification enumSpec) {
+// this.enumSpec = enumSpec;
+// }
+//
+// @Override
+// public DataSpecification getRefContract() {return dataSpec;}
+// public void setRefContract(DataSpecification dataSpec) {
+// this.dataSpec = dataSpec;
+// }
+//
+// @Override
+// public boolean isRepeatable() {return isList;}
+// public void setIsList(boolean isList) {this.isList = isList;}
+//
+// @Override
+// public int getMaxSize() {return maxLength;}
+// public void setMaxLength(int length) {
+// this.maxLength = maxLength;
+// }
+// @Override
+// public String getName() {return name;}
+// public void setName(String name) {
+// this.name = name;
+// }
+//
+// @Override
+// public String getDescription() {return description;}
+// public void setDescription(String description) {
+// this.description = description;
+// }
+//
+// @Override
+// public boolean isRefPubKey() {
+// return isRefPubKey;
+// }
+// public void setRefPubKey(boolean pubKey) {
+// this.isRefPubKey = pubKey;
+// }
+//
+// @Override
+// public boolean isRefPrivKey() {
+// return isRefPrivKey;
+// }
+// public void setRefPrivKey(boolean privKey) {
+// this.isRefPrivKey = privKey;
+// }
+//
+// @Override
+// public boolean isRefSignatureDigest() {
+// return isRefSignatureDigest;
+// }
+// public void setRefSignatureDigest(boolean signatureDigest) {
+// this.isRefSignatureDigest = signatureDigest;
+// }
+//
+// @Override
+// public boolean isRefHashDigest() {
+// return isRefHashDigest;
+// }
+// public void setRefHashDigest(boolean hashDigest) {
+// this.isRefHashDigest = hashDigest;
+// }
+//
+// @Override
+// public Class> getContractTypeResolver() {
+// return this.contractTypeResolver;
+// }
+// public void setContractTypeResolver(Class> resolver) {
+// this.contractTypeResolver = resolver;
+// }
+//
+// @Override
+// public boolean isRefIdentity() {
+// return isRefIdentity;
+// }
+// public void setRefIdentity(boolean identity) {
+// this.isRefIdentity = identity;
+// }
+//
+// @Override
+// public boolean isRefNetworkAddr() {
+// return isRefNetworkAddr;
+// }
+// public void setRefNetworkAddr(boolean networkAddr) {
+// this.isRefNetworkAddr = networkAddr;
+// }
+//}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/AbstractDynamicValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/AbstractDynamicValueConverter.java
new file mode 100644
index 00000000..d4ca0593
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/AbstractDynamicValueConverter.java
@@ -0,0 +1,46 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesInputStream;
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.NumberMask;
+
+public abstract class AbstractDynamicValueConverter implements DynamicValueConverter {
+
+ protected Class> valueType;
+
+ private static final NumberMask SIZE_HEAD = NumberMask.NORMAL;
+
+ private static final byte[] NULL_HEAD = new byte[1];
+
+ static {
+ SIZE_HEAD.writeMask(0, NULL_HEAD, 0);
+ }
+
+ public AbstractDynamicValueConverter(Class> valueType) {
+ this.valueType = valueType;
+ }
+
+ @Override
+ public Class> getValueType() {
+ return valueType;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return null;
+ }
+
+ protected int writeSize(int size, BytesOutputBuffer buffer) {
+ int len = SIZE_HEAD.getMaskLength(size);
+ byte[] headerBytes = new byte[len];
+ SIZE_HEAD.writeMask(size, headerBytes, 0);
+ buffer.write(headerBytes);
+ return len;
+ }
+
+ protected int readSize(BytesInputStream bytesStream) {
+ return SIZE_HEAD.resolveMaskedNumber(bytesStream);
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/AbstractFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/AbstractFieldEncoder.java
new file mode 100644
index 00000000..6a17f8d7
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/AbstractFieldEncoder.java
@@ -0,0 +1,50 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import com.jd.blockchain.binaryproto.BinarySliceSpec;
+import com.jd.blockchain.binaryproto.FieldSpec;
+
+public abstract class AbstractFieldEncoder implements FieldEncoder {
+
+ protected BinarySliceSpec sliceSpec;
+
+ protected FieldSpec fieldSpec;
+
+ protected Method reader;
+
+ public AbstractFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader) {
+ this.sliceSpec = sliceSpec;
+ this.fieldSpec = fieldSpec;
+ this.reader = reader;
+ }
+
+ @Override
+ public BinarySliceSpec getSliceSpecification() {
+ return sliceSpec;
+ }
+
+ @Override
+ public FieldSpec getFieldSpecification() {
+ return fieldSpec;
+ }
+
+ @Override
+ public Method getReader() {
+ return reader;
+ }
+
+
+ protected Object readValue(Object dataContract) {
+ try {
+ return reader.invoke(dataContract);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ protected Object[] readArrayValue(Object dataContract) {
+ return (Object[]) readValue(dataContract);
+ }
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BoolConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BoolConverter.java
new file mode 100644
index 00000000..0b32e040
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BoolConverter.java
@@ -0,0 +1,28 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+
+public class BoolConverter implements FixedValueConverter{
+
+ @Override
+ public Class> getValueType() {
+ return boolean.class;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return false;
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ buffer[offset] = ((Boolean)value).booleanValue() ? (byte)1 : (byte)0;
+ return 1;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getByte() == 1 ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BoolWrapperConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BoolWrapperConverter.java
new file mode 100644
index 00000000..54592434
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BoolWrapperConverter.java
@@ -0,0 +1,28 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+
+public class BoolWrapperConverter implements FixedValueConverter{
+
+ @Override
+ public Class> getValueType() {
+ return Boolean.class;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return null;
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ buffer[offset] = ((Boolean)value).booleanValue() ? (byte)1 : (byte)0;
+ return 1;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getByte() == 1 ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BytesSerializableValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BytesSerializableValueConverter.java
new file mode 100644
index 00000000..c0ad5acd
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BytesSerializableValueConverter.java
@@ -0,0 +1,55 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import com.jd.blockchain.binaryproto.DataContractException;
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSerializable;
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public class BytesSerializableValueConverter extends AbstractDynamicValueConverter {
+
+ private Constructor> constructor;
+
+ public BytesSerializableValueConverter(Class> valueType) {
+ super(valueType);
+ if (!BytesSerializable.class.isAssignableFrom(valueType)) {
+ throw new IllegalArgumentException("The specified type cann't be assigned as BytesSerializable!");
+ }
+ // 检查是否存在以 byte[] 为参数的构造器;
+ try {
+ constructor = valueType.getConstructor(byte[].class);
+ constructor.setAccessible(true);
+ } catch (NoSuchMethodException e) {
+ throw new DataContractException("No constructor with byte's array argument! --" + e.getMessage(), e);
+ } catch (SecurityException e) {
+ throw new DataContractException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ if (dataSlice.getSize() == 0) {
+ return null;
+ }
+ byte[] bytes = dataSlice.getBytesCopy();
+ try {
+ return constructor.newInstance(bytes);
+ } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
+ | InvocationTargetException e) {
+ throw new DataContractException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) {
+ byte[] bytes = value == null ? BytesUtils.EMPTY_BYTES : ((BytesSerializable) value).toBytes();
+ int size = bytes.length;
+ size += writeSize(size, buffer);
+ buffer.write(bytes);
+ return size;
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BytesValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BytesValueConverter.java
new file mode 100644
index 00000000..27664b6c
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BytesValueConverter.java
@@ -0,0 +1,28 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public class BytesValueConverter extends AbstractDynamicValueConverter {
+
+ public BytesValueConverter() {
+ super(byte[].class);
+ }
+
+ @Override
+ public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) {
+ byte[] bytes =value == null ? BytesUtils.EMPTY_BYTES : (byte[]) value;
+ int size = bytes.length;
+ size += writeSize(size, buffer);
+
+ buffer.write(bytes);
+ return size;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getBytesCopy();
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractContext.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractContext.java
new file mode 100644
index 00000000..82bdf68b
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractContext.java
@@ -0,0 +1,790 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.jd.blockchain.binaryproto.BinarySliceSpec;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataContractEncoder;
+import com.jd.blockchain.binaryproto.DataContractException;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.binaryproto.DataSpecification;
+import com.jd.blockchain.binaryproto.EnumContract;
+import com.jd.blockchain.binaryproto.EnumField;
+import com.jd.blockchain.binaryproto.EnumSpecification;
+import com.jd.blockchain.binaryproto.FieldSpec;
+import com.jd.blockchain.binaryproto.impl2.EnumSpecificationInfo.EnumConstant;
+import com.jd.blockchain.utils.ValueType;
+import com.jd.blockchain.utils.io.BytesSerializable;
+import com.jd.blockchain.utils.io.BytesUtils;
+import com.jd.blockchain.utils.security.SHA256Hash;
+import com.jd.blockchain.utils.security.ShaUtils;
+
+public class DataContractContext {
+
+ public static DataContractEncoderLookup ENCODER_LOOKUP;
+
+ private static final Object MUTEX = new Object();
+
+ private static final BinarySliceSpec HEAD_SLICE = BinarySliceSpec.newFixedSlice(HeaderEncoder.HEAD_BYTES, "HEAD",
+ "The code and version of data contract.");
+
+ private static final byte SINGLE_TYPE = 0;
+ private static final byte REPEATABLE_TYPE = 1;
+
+ /**
+ * 基本类型的字段;
+ */
+ private static final byte PRIMITIVE_TYPE_FIELD = 0;
+
+ /**
+ * 枚举类型的字段;
+ */
+ private static final byte ENUM_CONTRACT_FIELD = 1;
+
+ /**
+ * 引用一个具体的数据契约类型的字段;
+ */
+ private static final byte DATA_CONTRACT_FIELD = 2;
+
+ /**
+ * 动态的数据契约类型的字段;
+ */
+ private static final byte DYNAMIC_CONTRACT_FIELD = 3;
+
+ private static Map codeMap = new ConcurrentHashMap<>();
+ private static Map, DataContractEncoder> typeMap = new ConcurrentHashMap<>();
+
+ private static Map, EnumSpecification> enumContractSpecMap = new ConcurrentHashMap<>();
+
+ private static Map, ValueConverter>> primitiveTypeConverters = new HashMap<>();
+
+ static {
+ addConverterMapping(ValueType.BOOLEAN, boolean.class, new BoolConverter());
+ addConverterMapping(ValueType.BOOLEAN, Boolean.class, new BoolWrapperConverter());
+ addConverterMapping(ValueType.INT8, byte.class, new Int8ByteConverter());
+ addConverterMapping(ValueType.INT8, Byte.class, new Int8ByteWrapperConverter());
+ addConverterMapping(ValueType.INT16, short.class, new Int16ShortConverter());
+ addConverterMapping(ValueType.INT16, Short.class, new Int16ShortWrapperConverter());
+ addConverterMapping(ValueType.INT16, char.class, new Int16CharConverter());
+ addConverterMapping(ValueType.INT16, Character.class, new Int16CharWrapperConverter());
+ addConverterMapping(ValueType.INT32, int.class, new Int32IntConverter());
+ addConverterMapping(ValueType.INT32, Integer.class, new Int32IntWrapperConverter());
+ addConverterMapping(ValueType.INT64, long.class, new Int64LongConverter());
+ addConverterMapping(ValueType.INT64, Long.class, new Int64LongWrapperConverter());
+ addConverterMapping(ValueType.TEXT, String.class, new StringValueConverter());
+ addConverterMapping(ValueType.BYTES, byte[].class, new BytesValueConverter());
+
+ ENCODER_LOOKUP = new DataContractEncoderLookup() {
+ @Override
+ public DataContractEncoder lookup(int code, long version) {
+ ContractTypeVersionContext ctx = codeMap.get(code);
+ if (ctx == null) {
+ return null;
+ }
+ // TODO: 未实现多个版本的处理;
+ return ctx.contractEncoder;
+ }
+
+ @Override
+ public DataContractEncoder lookup(Class> contractType) {
+ return typeMap.get(contractType);
+ }
+ };
+ }
+
+ private static void addConverterMapping(ValueType protocalType, Class> javaType, ValueConverter converter) {
+ Map, ValueConverter> converterMap = primitiveTypeConverters.get(protocalType);
+ if (converterMap == null) {
+ converterMap = new HashMap<>();
+ primitiveTypeConverters.put(protocalType, converterMap);
+ }
+ converterMap.put(javaType, converter);
+ }
+
+ private static ValueConverter getPrimitiveTypeConverter(ValueType protocalType, Class> javaType) {
+ Map, ValueConverter> converterMap = primitiveTypeConverters.get(protocalType);
+ if (converterMap != null) {
+ ValueConverter converter = converterMap.get(javaType);
+ if (converter != null) {
+ return converter;
+ }
+ if (ValueType.BYTES == protocalType && BytesSerializable.class.isAssignableFrom(javaType)) {
+ converter = new BytesSerializableValueConverter(javaType);
+ converterMap.put(javaType, converter);
+ return converter;
+ }
+ }
+ throw new IllegalArgumentException(String.format("Unsupport types mapping: [PrimitiveType=%s]-[JavaType=%s]",
+ protocalType.toString(), javaType.toString()));
+ }
+
+ public static DataContractEncoder resolve(Class> contractType) {
+ DataContractEncoder encoder = typeMap.get(contractType);
+ if (encoder != null) {
+ return encoder;
+ }
+ synchronized (MUTEX) {
+ encoder = typeMap.get(contractType);
+ if (encoder != null) {
+ return encoder;
+ }
+ ContractTypeVersionContext ctx = resolveContract(contractType);
+ encoder = ctx.contractEncoder;
+ }
+
+ return encoder;
+ }
+
+ /**
+ * 解析数据契约;
+ *
+ * @param contractType
+ * @return
+ */
+ private static ContractTypeVersionContext resolveContract(Class> contractType) {
+ // TODO: 未处理可能存在的循环依赖问题,这会导致解析方法陷入死循环;
+ if (!contractType.isInterface()) {
+ throw new IllegalArgumentException(
+ "The specified contractType [" + contractType.toString() + "] is not a interface!");
+ }
+ DataContract annoContract = contractType.getAnnotation(DataContract.class);
+ if (annoContract == null) {
+ throw new IllegalArgumentException(
+ "Class[" + contractType.toString() + "] isn't annotated as DataContract!");
+ }
+ int contractCode = annoContract.code();
+ ContractTypeVersionContext ctx = codeMap.get(contractCode);
+ if (ctx != null) {
+ if (ctx.contractType == contractType) {
+ return ctx;
+ } else {
+ throw new IllegalStateException(String.format(
+ "Contract Code[%s] has been registered by type[%s]! Cann't register again with type[%s]!",
+ contractCode, ctx.contractType.getName(), contractType.getName()));
+ }
+ }
+
+ DataContractEncoder contractEncoder = resolveEncoder(contractType, annoContract);
+
+ ctx = new ContractTypeVersionContext(contractType, contractEncoder);
+
+ codeMap.put(contractCode, ctx);
+ typeMap.put(contractType, contractEncoder);
+
+ return ctx;
+ }
+
+ /**
+ * @param contractType
+ * @param annoContract
+ * @return
+ */
+ private static DataContractEncoder resolveEncoder(Class> contractType, DataContract annoContract) {
+ DataContractEncoder encoder = typeMap.get(contractType);
+ if (encoder != null) {
+ return encoder;
+ }
+
+ if (!contractType.isInterface()) {
+ throw new IllegalArgumentException(
+ "The registering contract type is not a interface! --" + contractType.getName());
+ }
+
+ // 解析获得数据契约的有序的字段列表;
+ List allFields = resolveContractFields(contractType, annoContract);
+
+ // 解析每一个字段,生成字段的编码器和二进制片段描述符;
+ FieldSpecInfo[] fieldSpecs = new FieldSpecInfo[allFields.size()];
+ BinarySliceSpec[] dataSliceSpecs = new BinarySliceSpec[allFields.size() + 1];
+ FieldEncoder[] fieldEncoders = new FieldEncoder[allFields.size()];
+
+ dataSliceSpecs[0] = HEAD_SLICE;
+
+ SHA256Hash versionHash = ShaUtils.hash_256();// 用于计算 DataContract 的版本号的哈希生成器;
+ int i = 0;
+ for (FieldDeclaredInfo fieldInfo : allFields) {
+ fieldSpecs[i] = fieldInfo.fieldSpec;
+
+ // 构建二进制片段;
+ dataSliceSpecs[i + 1] = buildSlice(fieldInfo.fieldSpec);
+
+ fieldEncoders[i] = buildFieldEncoder(fieldInfo, dataSliceSpecs[i + 1]);
+
+ // 按顺序计算字段类型,
+ byte[] fieldType = generateFieldTypeCode(fieldInfo.fieldSpec);
+ versionHash.update(fieldType);
+
+ i++;
+ }
+
+ // 数据契约的版本号取自对所有字段的数据类型的哈希前 8 位;
+ byte[] allFieldTypesHash = versionHash.complete();
+ long version = BytesUtils.toLong(allFieldTypesHash);
+
+ HeaderEncoder headerEncoder = new HeaderEncoder(HEAD_SLICE, annoContract.code(), version, annoContract.name(),
+ annoContract.description());
+
+ DataContractSpecification spec = new DataContractSpecification(annoContract.code(), version,
+ annoContract.name(), annoContract.description(), dataSliceSpecs, fieldSpecs);
+
+ DataContractEncoderImpl contractEncoder = new DataContractEncoderImpl(contractType, spec, headerEncoder,
+ fieldEncoders);
+
+ return contractEncoder;
+ }
+
+ /**
+ * 解析获得数据契约的有序的字段列表;
+ *
+ * @param contractType
+ * @param annoContract
+ * @return
+ */
+ private static List resolveContractFields(Class> contractType, DataContract annoContract) {
+ // 解析每一个方法,获得标注的合约字段,并按照声明的合约类型进行分组;
+ Map, DeclaredFieldGroup> declaredFielGroups = new HashMap<>();
+ Method[] methods = contractType.getMethods();
+ for (Method method : methods) {
+ DataField annoField = method.getAnnotation(DataField.class);
+ if (annoField == null) {
+ continue;
+ }
+ Class> declaredType = method.getDeclaringClass();
+ DeclaredFieldGroup group = declaredFielGroups.get(declaredType);
+ if (group != null) {
+ FieldSpecInfo fieldSpec = resolveFieldSpec(method, annoField);
+ group.addField(method, annoField, fieldSpec);
+ continue;
+ }
+ if (declaredType == contractType) {
+ // 字段是由当前的数据契约类型所声明;
+ FieldSpecInfo fieldSpec = resolveFieldSpec(method, annoField);
+ group = new DeclaredFieldGroup(contractType, annoContract, method, annoField, fieldSpec);
+ declaredFielGroups.put(contractType, group);
+ continue;
+ }
+ // 字段由父接口声明,所以取父接口上的标注定义进行解析;
+ DataContract declaredContractAnnotation = declaredType.getAnnotation(DataContract.class);
+ if (declaredContractAnnotation == null) {
+ throw new DataContractException("Declare data contract field in a non-data-contract type! --[Type="
+ + declaredType.getName() + "]");
+ }
+
+ FieldSpecInfo fieldSpec = resolveFieldSpec(method, annoField);
+ group = new DeclaredFieldGroup(declaredType, declaredContractAnnotation, method, annoField, fieldSpec);
+ declaredFielGroups.put(declaredType, group);
+ }
+
+ DeclaredFieldGroup[] groups = declaredFielGroups.values()
+ .toArray(new DeclaredFieldGroup[declaredFielGroups.size()]);
+ for (DeclaredFieldGroup group : groups) {
+ // 计算继承距离;
+ int extendsionDistance = computeExtendsionDistance(contractType, group.declaredContractType);
+ if (extendsionDistance < 0) {
+ // 实际不会进入此分支;
+ throw new IllegalStateException("Illegal state that isn't expected to occur!");
+ }
+ group.setExtendsionDistance(extendsionDistance);
+ }
+
+ // 按继承距离和数据契约的编码进行倒序排序,如果继承距离相同,则编码小的在前;
+ // 达到的效果:父接口声明的字段在前,子接口声明的字段在后;同一个继承级别,则编码小的在前;
+ Arrays.sort(groups,
+ (g1, g2) -> (g2.extendsionDistance == g1.extendsionDistance
+ ? g1.declaredContractAnnotation.code() - g2.declaredContractAnnotation.code()
+ : g2.extendsionDistance - g1.extendsionDistance));
+
+ List allFields = new ArrayList<>();
+ for (DeclaredFieldGroup grp : groups) {
+ allFields.addAll(grp.getFields());
+ }
+
+ return allFields;
+ }
+
+ /**
+ * 创建字段的编码器;
+ *
+ * @param fieldInfo
+ * @param sliceSpec
+ * @return
+ */
+ private static FieldEncoder buildFieldEncoder(FieldDeclaredInfo fieldInfo, BinarySliceSpec sliceSpec) {
+ FieldSpecInfo fieldSpec = fieldInfo.fieldSpec;
+ if (fieldSpec.getPrimitiveType() != null) {
+ return buildPrimitiveFieldEncoder(fieldInfo, sliceSpec);
+ } else if (fieldSpec.getRefEnum() != null) {
+ return buildEnumFieldEncoder(fieldInfo, sliceSpec);
+ } else if (fieldSpec.getRefContract() != null) {
+ return buildContractFieldEncoder(fieldInfo, sliceSpec);
+ } else {
+ throw new IllegalStateException("Illegal states that has no type definition for field! --[ReadMethod="
+ + fieldInfo.reader.toString() + "");
+ }
+ }
+
+ /**
+ * 创建数据契约引用字段的编码器;
+ *
+ * @param fieldInfo
+ * @param sliceSpec
+ * @return
+ */
+ private static FieldEncoder buildContractFieldEncoder(FieldDeclaredInfo fieldInfo, BinarySliceSpec sliceSpec) {
+ ValueConverter valueConverter;
+ if (fieldInfo.fieldSpec.isGenericContract()) {
+ Class> contractType = fieldInfo.fieldSpec.getDataType();
+ valueConverter = new DataContractGenericRefConverter(contractType, ENCODER_LOOKUP);
+ } else {
+ Class> contractType = fieldInfo.fieldSpec.getDataType();
+ DataContractEncoder encoder = typeMap.get(contractType);
+ valueConverter = new DataContractValueConverter(encoder);
+ }
+
+ return createFieldEncoder(sliceSpec, fieldInfo.fieldSpec, fieldInfo.reader, valueConverter);
+ }
+
+ /**
+ * 创建枚举类型的字段编码器;
+ *
+ * @param fieldInfo
+ * @param sliceSpec
+ * @return
+ */
+ private static FieldEncoder buildEnumFieldEncoder(FieldDeclaredInfo fieldInfo, BinarySliceSpec sliceSpec) {
+ // 枚举类型的值转换器是由枚举值的范围检查加上一个基本类型的值转换器组成;
+ Class> enumType = fieldInfo.fieldSpec.getDataType();
+ EnumSpecificationInfo enumSpec = (EnumSpecificationInfo) fieldInfo.fieldSpec.getRefEnum();
+ int[] values = enumSpec.getItemValues();
+ Object[] constants = enumSpec.getConstants();
+ ValueType codeType = enumSpec.getValueType();
+
+ ValueConverter baseConverter = getPrimitiveTypeConverter(codeType, enumSpec.getDataType());
+
+ EnumValueConverter valueConverter = new EnumValueConverter(enumType, codeType, values, constants,
+ (FixedValueConverter) baseConverter);
+
+ return createFieldEncoder(sliceSpec, fieldInfo.fieldSpec, fieldInfo.reader, valueConverter);
+ }
+
+ /**
+ * 创建基本类型字段的编码器;
+ *
+ * @param fieldInfo
+ * @param sliceSpec
+ * @return
+ */
+ private static FieldEncoder buildPrimitiveFieldEncoder(FieldDeclaredInfo fieldInfo, BinarySliceSpec sliceSpec) {
+ ValueConverter valueConverter = getPrimitiveTypeConverter(fieldInfo.fieldSpec.getPrimitiveType(),
+ fieldInfo.fieldSpec.getDataType());
+ return createFieldEncoder(sliceSpec, fieldInfo.fieldSpec, fieldInfo.reader, valueConverter);
+ }
+
+ private static FieldEncoder createFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader,
+ ValueConverter valueConverter) {
+ if (sliceSpec.isRepeatable()) {
+ if (sliceSpec.isDynamic()) {
+ return new DynamicArrayFieldEncoder(sliceSpec, fieldSpec, reader,
+ (DynamicValueConverter) valueConverter);
+ } else {
+ return new FixedArrayFieldEncoder(sliceSpec, fieldSpec, reader, (FixedValueConverter) valueConverter);
+ }
+ } else {
+ if (sliceSpec.isDynamic()) {
+ return new DynamicFieldEncoder(sliceSpec, fieldSpec, reader, (DynamicValueConverter) valueConverter);
+ } else {
+ return new FixedFieldEncoder(sliceSpec, fieldSpec, reader, (FixedValueConverter) valueConverter);
+ }
+ }
+ }
+
+ private static BinarySliceSpec buildSlice(FieldSpecInfo fieldSpec) {
+ boolean fixed = false;
+ int len = -1;
+ ValueType fixedValueType = null;
+ if (fieldSpec.getPrimitiveType() != null && fieldSpec.getPrimitiveType() != ValueType.NIL) {
+ fixedValueType = fieldSpec.getPrimitiveType();
+ } else if (fieldSpec.getRefEnum() != null) {
+ fixedValueType = fieldSpec.getRefEnum().getValueType();
+ }
+
+ if (fixedValueType != null) {
+ switch (fixedValueType) {
+ case BOOLEAN:
+ fixed = true;
+ len = 1;
+ break;
+ case INT8:
+ fixed = true;
+ len = 1;
+ break;
+ case INT16:
+ fixed = true;
+ len = 2;
+ break;
+ case INT32:
+ fixed = true;
+ len = 4;
+ break;
+ case INT64:
+ fixed = true;
+ len = 8;
+ break;
+ default:
+ break;
+ }
+ }
+ if (fieldSpec.isRepeatable()) {
+ if (fixed) {
+ return BinarySliceSpec.newRepeatableFixedSlice(len, fieldSpec.getName(), fieldSpec.getDescription());
+ } else {
+ return BinarySliceSpec.newRepeatableDynamicSlice(fieldSpec.getName(), fieldSpec.getDescription());
+ }
+ } else {
+ if (fixed) {
+ return BinarySliceSpec.newFixedSlice(len, fieldSpec.getName(), fieldSpec.getDescription());
+ } else {
+ return BinarySliceSpec.newDynamicSlice(fieldSpec.getName(), fieldSpec.getDescription());
+ }
+ }
+ }
+
+ private static byte[] generateFieldTypeCode(FieldSpecInfo fieldSpec) {
+ byte repeatable = fieldSpec.isRepeatable() ? REPEATABLE_TYPE : SINGLE_TYPE;
+ byte[] codeBytes;
+ if (fieldSpec.getPrimitiveType() != null) {
+ // repeatable + type indicator + code of primitive type;
+ // 1 + 1 + 4;
+ codeBytes = new byte[6];
+ codeBytes[0] = repeatable;
+ codeBytes[1] = PRIMITIVE_TYPE_FIELD;
+ BytesUtils.toBytes(fieldSpec.getPrimitiveType().CODE, codeBytes, 2);
+ } else if (fieldSpec.getRefEnum() != null) {
+ // repeatable + type indicator + code of enum contract + version of enum
+ // contract;
+ // 1+ 1 + 4 + 8;
+ codeBytes = new byte[14];
+ codeBytes[0] = repeatable;
+ codeBytes[1] = ENUM_CONTRACT_FIELD;
+ EnumSpecification enumSpec = fieldSpec.getRefEnum();
+ BytesUtils.toBytes(enumSpec.getCode(), codeBytes, 2);
+ BytesUtils.toBytes(enumSpec.getVersion(), codeBytes, 6);
+ } else if (fieldSpec.getRefContract() != null) {
+ // repeatable + type indicator + code of enum contract + version of enum
+ // contract;
+ // 1+ 1 + 4 + 8;
+ DataSpecification dataSpec = fieldSpec.getRefContract();
+ codeBytes = new byte[14];
+ codeBytes[0] = repeatable;
+ if (fieldSpec.isGenericContract()) {
+ codeBytes[1] = DYNAMIC_CONTRACT_FIELD;
+ } else {
+ codeBytes[1] = DATA_CONTRACT_FIELD;
+ }
+ BytesUtils.toBytes(dataSpec.getCode(), codeBytes, 2);
+ BytesUtils.toBytes(dataSpec.getVersion(), codeBytes, 6);
+ } else {
+ throw new DataContractException("Unknow field type!");
+ }
+ return codeBytes;
+ }
+
+ /**
+ * 计算指定两个接口类型之间的继承距离;
+ *
+ * 如果不具有继承关系,则返回 -1;
+ *
+ * @param subsTypes
+ * 子类型;
+ * @param superType
+ * 父类型;
+ * @return
+ */
+ private static int computeExtendsionDistance(Class> subsTypes, Class> superType) {
+ if (subsTypes == superType) {
+ return 0;
+ }
+ Class>[] superIntfs = subsTypes.getInterfaces();
+ for (Class> si : superIntfs) {
+ int dis = computeExtendsionDistance(si, superType);
+ if (dis > -1) {
+ return dis + 1;
+ }
+ }
+ return -1;
+ }
+
+ private static FieldSpecInfo resolveFieldSpec(Method accessor, DataField annoField) {
+ String name = annoField.name();
+ name = name == null ? null : name.trim();
+ if (name == null || name.length() > 0) {
+ name = accessor.getName();
+ }
+ String desc = annoField.decription();
+ desc = desc == null ? null : desc.trim();
+
+ int order = annoField.order();
+
+ boolean repeatable = annoField.list();
+ Class> dataType = accessor.getReturnType();
+
+ if (repeatable) {
+ if (!dataType.isArray()) {
+ throw new DataContractException("The annotated repeatable type mismatch non-array type["
+ + dataType.getName() + "]! --[Field=" + accessor.toString() + "]");
+ }
+ dataType = dataType.getComponentType();
+ }
+
+ int maxSize = annoField.maxSize();
+ ValueType primitiveType = annoField.primitiveType();
+ if (primitiveType != null) {
+ primitiveType = verifyPrimitiveType(primitiveType, dataType, accessor);
+ }
+
+ boolean refEnum = annoField.refEnum();
+ EnumSpecification enumSpecification = null;
+ if (refEnum) {
+ EnumContract annoEnumContract = dataType.getAnnotation(EnumContract.class);
+ if (annoEnumContract == null) {
+ throw new DataContractException("The data type of annotated enum field is not a EnumContract! --[Field="
+ + accessor.toString() + "]");
+ }
+ enumSpecification = resolveEnumContract(dataType, annoEnumContract);
+ }
+
+ boolean refContract = annoField.refContract();
+ DataSpecification contractSpecification = null;
+ // Class> contractTypeResolverClazz = null;
+ if (refContract) {
+ // DataContract annoContract = dataType.getAnnotation(DataContract.class);
+ // if (annoContract == null) {
+ // throw new DataContractException(
+ // "The data type of annotated contract field is not a DataContract! --[Field="
+ // + accessor.toString() + "]");
+ // }
+
+ ContractTypeVersionContext contractContext = resolveContract(dataType);
+ DataContractEncoder encoder = contractContext.contractEncoder;
+ contractSpecification = encoder.getSepcification();
+ // contractTypeResolverClazz = annoField.contractTypeResolver();
+ // if (contractTypeResolverClazz != null
+ // && (!ContractTypeResolver.class.isAssignableFrom(contractTypeResolverClazz)))
+ // {
+ // throw new DataContractException(
+ // "The contract type resolver of contract field doesn't implement
+ // ContractTypeResolver interface! --[Field="
+ // + accessor.toString() + "]");
+ // }
+ }
+
+ if (primitiveType == null && enumSpecification == null && contractSpecification == null) {
+ throw new DataContractException(
+ "Miss data type definition of field! --[Field=" + accessor.toString() + "]");
+ }
+
+ FieldSpecInfo fieldSpec = null;
+ if (primitiveType != null) {
+ fieldSpec = new FieldSpecInfo(order, name, desc, primitiveType, repeatable, maxSize, dataType);
+ } else if (enumSpecification != null) {
+ fieldSpec = new FieldSpecInfo(order, name, desc, enumSpecification, repeatable, dataType);
+ } else {
+ fieldSpec = new FieldSpecInfo(order, name, desc, contractSpecification, repeatable, dataType,
+ annoField.genericContract());
+ }
+ return fieldSpec;
+ }
+
+ private static EnumSpecification resolveEnumContract(Class> dataType, EnumContract annoEnumContract) {
+ EnumSpecificationInfo enumSpec = (EnumSpecificationInfo) enumContractSpecMap.get(dataType);
+ if (enumSpec != null) {
+ return enumSpec;
+ }
+ try {
+ if (!dataType.isEnum()) {
+ throw new DataContractException("Field's type is not a enum type! --[" + dataType.toString() + "]");
+ }
+ // TODO:暂时硬编码检索 code 字段;
+ Field codeField = dataType.getField("CODE");
+ if (codeField == null) {
+ throw new DataContractException("Enum type miss the 'CODE' field! --[" + dataType.toString() + "]");
+ }
+ EnumField fieldAnno = codeField.getAnnotation(EnumField.class);
+ if (fieldAnno == null) {
+ throw new DataContractException("Enum's 'CODE' field is not annotated with @EnumField !");
+ }
+ // TODO: 暂时未实现枚举契约的版本号计算;
+ long version = 0;
+ enumSpec = new EnumSpecificationInfo(fieldAnno.type(), annoEnumContract.code(), version,
+ annoEnumContract.name(), annoEnumContract.decription(), codeField.getType());
+ // get enum constants and CODE
+ Object[] enumItems = dataType.getEnumConstants();
+ for (Object item : enumItems) {
+ int code = codeField.getInt(item);
+ EnumConstant constant = new EnumConstant(code, item.toString(), item);
+ enumSpec.addConstant(constant);
+ // enumSpec.setVersion(); if need
+ }
+ enumContractSpecMap.put(dataType, enumSpec);
+ return enumSpec;
+ } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
+ throw new DataContractException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 解析指定的类型是否匹配;
+ *
+ * 要求必须是显式地声明类型,因此不会根据 Java 语言的声明类型做自动转换;
+ *
+ * @param primitiveType
+ * @param dataType
+ * @return
+ */
+ private static ValueType verifyPrimitiveType(ValueType primitiveType, Class> dataType, Method accessor) {
+ switch (primitiveType) {
+ case NIL:
+ return null;
+ case BOOLEAN:
+ if (dataType != Boolean.class && dataType != boolean.class) {
+ throw new DataContractException("The annotated primitive type[" + primitiveType.toString()
+ + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]");
+ }
+ break;
+ case INT8:
+ if (dataType != Byte.class && dataType != byte.class) {
+ throw new DataContractException("The annotated primitive type[" + primitiveType.toString()
+ + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]");
+ }
+ break;
+ case INT16:
+ if (dataType != Character.class && dataType != char.class && dataType != short.class) {
+ throw new DataContractException("The annotated primitive type[" + primitiveType.toString()
+ + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]");
+ }
+ break;
+ case INT32:
+ if (dataType != Integer.class && dataType != int.class) {
+ throw new DataContractException("The annotated primitive type[" + primitiveType.toString()
+ + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]");
+ }
+ break;
+ case INT64:
+ if (dataType != Long.class && dataType != long.class) {
+ throw new DataContractException("The annotated primitive type[" + primitiveType.toString()
+ + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]");
+ }
+ break;
+ case TEXT:
+ if (dataType != String.class) {
+ throw new DataContractException("The annotated primitive type[" + primitiveType.toString()
+ + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]");
+ }
+ break;
+ case BYTES:
+ if (dataType != byte[].class && (!BytesSerializable.class.isAssignableFrom(dataType))) {
+ throw new DataContractException("The annotated primitive type[" + primitiveType.toString()
+ + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]");
+ }
+ break;
+ default:
+ throw new DataContractException("Unsupported primitive type[" + primitiveType.toString() + "] ! --[Field="
+ + accessor.toString() + "]");
+ }
+
+ return primitiveType;
+ }
+
+ private static class DeclaredFieldGroup {
+
+ /**
+ * 声明的合约类型;
+ */
+ public Class> declaredContractType;
+
+ /**
+ *
+ */
+ public DataContract declaredContractAnnotation;
+
+ /**
+ * 声明类型距离要解析的类型的继承距离;直接继承的父接口的距离为 1,父接口的父接口为 2,以此类推;
+ */
+ private int extendsionDistance;
+
+ private TreeMap orderedFields = new TreeMap<>();
+
+ public Collection getFields() {
+ return orderedFields.values();
+ }
+
+ public DeclaredFieldGroup(Class> declaredContractType, DataContract declaredContractAnnotation,
+ Method accessor, DataField fieldAnnotation, FieldSpecInfo fieldSpec) {
+ this.declaredContractType = declaredContractType;
+ this.declaredContractAnnotation = declaredContractAnnotation;
+
+ addField(accessor, fieldAnnotation, fieldSpec);
+ }
+
+ private void addField(Method accessor, DataField fieldAnnotation, FieldSpecInfo fieldSpec) {
+ // 检查字段的是否有重复序号;
+ FieldDeclaredInfo fieldInfo = new FieldDeclaredInfo(accessor, fieldAnnotation, fieldSpec);
+ FieldDeclaredInfo conflictedField = orderedFields.put(fieldSpec.getOrder(), fieldInfo);
+ if (conflictedField != null) {
+ // 有两个字段都声明了相同的序号,这容易导致无序状态;
+ throw new DataContractException(String.format("Declare two fields with the same order! --[%s][%s]",
+ fieldInfo.reader.toString(), conflictedField.reader.toString()));
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public int getExtendsionDistance() {
+ return extendsionDistance;
+ }
+
+ public void setExtendsionDistance(int extendsionDistance) {
+ this.extendsionDistance = extendsionDistance;
+ }
+
+ }
+
+ private static class FieldDeclaredInfo {
+ public FieldSpecInfo fieldSpec;
+
+ public Method reader;
+
+ public DataField annoField;
+
+ public FieldDeclaredInfo(Method accessor, DataField annoField, FieldSpecInfo fieldSpec) {
+ this.reader = accessor;
+ this.annoField = annoField;
+ this.fieldSpec = fieldSpec;
+ }
+ }
+
+ private static class ContractTypeVersionContext {
+
+ public Class> contractType;
+
+ public DataContractEncoder contractEncoder;
+
+ // TODO:未实现多版本;
+ // private HashMap versionMap = new HashMap<>();
+
+ public ContractTypeVersionContext(Class> contractType, DataContractEncoder encoder) {
+ this.contractType = contractType;
+ this.contractEncoder = encoder;
+ }
+
+ }
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractEncoderImpl.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractEncoderImpl.java
new file mode 100644
index 00000000..2c9c40b6
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractEncoderImpl.java
@@ -0,0 +1,133 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.jd.blockchain.binaryproto.DataContractEncoder;
+import com.jd.blockchain.binaryproto.DataSpecification;
+import com.jd.blockchain.utils.io.BytesInputStream;
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlice;
+
+public class DataContractEncoderImpl implements DataContractEncoder {
+
+ private Class>[] contractTypeArray;
+
+ private Class> contractType;
+
+ private DataSpecification specification;
+
+ private HeaderEncoder headEncoder;
+
+ private FieldEncoder[] fieldEncoders;
+
+ // 字段的 Get 方法与编码器的映射表;
+ private Map fieldIndexMap;
+
+ /**
+ * @param contractType
+ * @param specification
+ * @param headEncoder
+ * 头部编码器;
+ * @param fieldEncoders
+ * 按顺序排列的字段编码器列表;
+ */
+ public DataContractEncoderImpl(Class> contractType, DataSpecification specification, HeaderEncoder headEncoder,
+ FieldEncoder[] fieldEncoders) {
+ this.contractType = contractType;
+ this.contractTypeArray = new Class>[] { contractType };
+ this.specification = specification;
+ this.headEncoder = headEncoder;
+ this.fieldEncoders = fieldEncoders;
+ this.fieldIndexMap = new HashMap<>();
+ int i = 0;
+ for (FieldEncoder fieldEncoder : fieldEncoders) {
+ fieldIndexMap.put(fieldEncoder.getReader(), i);
+ i++;
+ }
+ }
+
+ HeaderEncoder getHeaderEncoder() {
+ return headEncoder;
+ }
+
+ Class>[] getContractTypeAsArray() {
+ return contractTypeArray;
+ }
+
+ int getFieldCount() {
+ return fieldEncoders.length;
+ }
+
+ FieldEncoder getFieldEncoder(int id) {
+ return fieldEncoders[id];
+ }
+
+ /**
+ * 通过字段的声明方法返回字段的序号;
+ *
+ * @param declaredMethod
+ * 声明并标注为数据契约字段的方法;注:不能是覆盖的非标注方法,也不能是实现方法;
+ * @return 字段序号; 如果不存在,则返回 -1;
+ */
+ int getFieldId(Method declaredMethod) {
+ Integer id = fieldIndexMap.get(declaredMethod);
+ return id == null ? -1 : id.intValue();
+ }
+
+ /**
+ * 通过字段的声明方法返回字段的编码器;
+ *
+ * @param declaredMethod
+ * 声明并标注为数据契约字段的方法;注:不能是覆盖的非标注方法,也不能是实现方法;
+ * @return
+ */
+ FieldEncoder getFieldEncoder(Method declaredMethod) {
+ Integer idx = fieldIndexMap.get(declaredMethod);
+ if (idx == null) {
+ return null;
+ }
+ return fieldEncoders[idx.intValue()];
+ }
+
+ @Override
+ public DataSpecification getSepcification() {
+ return specification;
+ }
+
+ @Override
+ public Class> getContractType() {
+ return contractType;
+ }
+
+ @Override
+ public int encode(Object dataContract, BytesOutputBuffer buffer) {
+ int size = 0;
+ size += headEncoder.encode(dataContract, buffer);
+ if (dataContract != null) {
+ for (SliceEncoder sliceEncoder : fieldEncoders) {
+ size += sliceEncoder.encode(dataContract, buffer);
+ }
+ }
+ return size;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T decode(BytesInputStream bytesStream) {
+ BytesSlice headerSlice = bytesStream.getSlice(HeaderEncoder.HEAD_BYTES);
+ if (!headEncoder.verifyHeaders(headerSlice)) {
+ throw new IllegalArgumentException(String.format(
+ "The code and version resolved from bytes stream is not match this data contract encoder! --[expected=%s, %s][actual=%s, %s].",
+ headEncoder.getCode(), headEncoder.getVersion(), HeaderEncoder.resolveCode(headerSlice),
+ HeaderEncoder.resolveVersion(headerSlice)));
+ }
+ if (bytesStream.getSize() == HeaderEncoder.HEAD_BYTES) {
+ // 只有头部,没有值,表示空值;
+ return null;
+ }
+ return (T) DynamicDataContract.createContract(bytesStream, this);
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractEncoderLookup.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractEncoderLookup.java
new file mode 100644
index 00000000..41de58b0
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractEncoderLookup.java
@@ -0,0 +1,23 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.binaryproto.DataContractEncoder;
+
+public interface DataContractEncoderLookup {
+
+ /**
+ * 检索指定类型的编码器;
+ *
+ * @param contractType
+ * @return
+ */
+ DataContractEncoder lookup(Class> contractType);
+
+ /**
+ * 检索指定 code 和 version 的编码器;
+ *
+ * @param contractType
+ * @return
+ */
+ DataContractEncoder lookup(int code, long version);
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractGenericRefConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractGenericRefConverter.java
new file mode 100644
index 00000000..a87c4daa
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractGenericRefConverter.java
@@ -0,0 +1,94 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataContractEncoder;
+import com.jd.blockchain.binaryproto.DataContractException;
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlice;
+
+public class DataContractGenericRefConverter extends AbstractDynamicValueConverter {
+
+ private final Object mutex = new Object();
+
+ private DataContractEncoderLookup encoderLookup;
+
+ private Map, DataContractEncoder> encoderCache;
+
+ public DataContractGenericRefConverter(Class> baseType, DataContractEncoderLookup encoderLookup) {
+ super(baseType);
+ this.encoderLookup = encoderLookup;
+ this.encoderCache = new ConcurrentHashMap<>();
+ }
+
+ private DataContractEncoder lookupEncoder(Class> dataObjectType) {
+ DataContractEncoder encoder = encoderCache.get(dataObjectType);
+ if (encoder != null) {
+ return encoder;
+ }
+ synchronized (mutex) {
+ encoder = encoderCache.get(dataObjectType);
+ if (encoder != null) {
+ return encoder;
+ }
+ Class>[] intfs = dataObjectType.getInterfaces();
+ Class> contractType = null;
+ DataContract anno = null;
+ for (Class> itf : intfs) {
+ anno = itf.getAnnotation(DataContract.class);
+ if (anno != null) {
+ if (contractType == null) {
+ contractType = itf;
+ } else {
+ throw new DataContractException(String.format(
+ "Data object implements more than one DataContract interface! --[DataObject=%s]",
+ dataObjectType.toString()));
+ }
+ }
+ }
+ if (contractType == null) {
+ throw new DataContractException(
+ String.format("Data object doesn't implement any DataContract interface! --[DataObject=%s]",
+ dataObjectType.toString()));
+ }
+
+ encoder = encoderLookup.lookup(contractType);
+ if (encoder == null) {
+ throw new DataContractException(String.format(
+ "DataContract of the specified data object hasn't been registered! --[DataContract=%s][DataObject=%s]",
+ contractType.toString(), dataObjectType.toString()));
+ }
+ encoderCache.put(dataObjectType, encoder);
+ }
+
+ return encoder;
+ }
+
+ @Override
+ public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) {
+ DataContractEncoder contractEncoder = lookupEncoder(value.getClass());
+
+ BytesOutputBuffer contractBuffer = new BytesOutputBuffer();
+ int size = contractEncoder.encode(value, contractBuffer);
+
+ size += writeSize(size, buffer);
+
+ buffer.write(contractBuffer);
+ return size;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ int code = HeaderEncoder.resolveCode(dataSlice);
+ long version = HeaderEncoder.resolveVersion(dataSlice);
+ DataContractEncoder contractEncoder = encoderLookup.lookup(code, version);
+ if (contractEncoder == null) {
+ throw new DataContractException(
+ String.format("No data contract was registered with code[%s] and version[%s]!", code, version));
+ }
+ return contractEncoder.decode(dataSlice.getInputStream());
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractHeader.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractHeader.java
new file mode 100644
index 00000000..33700b65
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractHeader.java
@@ -0,0 +1,28 @@
+//package com.jd.blockchain.binaryproto.impl2;
+//
+//public class DataContractHeader {
+//
+// private int code;
+//
+// private int version;
+//
+// private String name;
+//
+// private String description;
+//
+// public DataContractHeader(int contractCode, int contractVersion, String name, String description) {
+// this.code = contractCode;
+// this.version = contractVersion;
+// this.name = name ;
+// this.description = description;
+// }
+//
+// public int getContractVersion() {
+// return version;
+// }
+//
+// public int getContractCode() {
+// return code;
+// }
+//
+//}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractSpecification.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractSpecification.java
new file mode 100644
index 00000000..7bdb2b65
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractSpecification.java
@@ -0,0 +1,65 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import com.jd.blockchain.binaryproto.BinarySliceSpec;
+import com.jd.blockchain.binaryproto.DataSpecification;
+import com.jd.blockchain.binaryproto.FieldSpec;
+
+public class DataContractSpecification implements DataSpecification {
+
+ private int code;
+ private long version;
+ private String name;
+ private String description;
+
+ private List fieldList;
+ private List sliceList;
+
+ public DataContractSpecification(int code, long version, String name, String description, BinarySliceSpec[] slices, FieldSpec[] fields) {
+ this.code = code;
+ this.version = version;
+ this.name = name;
+ this.description = description;
+ this.fieldList = Collections.unmodifiableList(Arrays.asList(fields));
+ this.sliceList = Collections.unmodifiableList(Arrays.asList(slices));
+ }
+
+ @Override
+ public int getCode() {
+ return code;
+ }
+
+ @Override
+ public long getVersion() {
+ return version;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public List getFields() {
+ return fieldList;
+ }
+
+ @Override
+ public List getSlices() {
+ return sliceList;
+ }
+
+ @Override
+ public String toHtml() {
+ throw new IllegalStateException("Not implemented!");
+ }
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractValueConverter.java
new file mode 100644
index 00000000..e9d9d401
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractValueConverter.java
@@ -0,0 +1,32 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.binaryproto.DataContractEncoder;
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlice;
+
+public class DataContractValueConverter extends AbstractDynamicValueConverter {
+
+ private DataContractEncoder contractEncoder;
+
+ public DataContractValueConverter(DataContractEncoder contractEncoder) {
+ super(contractEncoder.getContractType());
+ this.contractEncoder =contractEncoder;
+ }
+
+ @Override
+ public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) {
+ BytesOutputBuffer contractBuffer = new BytesOutputBuffer();
+ int size = contractEncoder.encode(value, contractBuffer);
+
+ size += writeSize(size, buffer);
+
+ buffer.write(contractBuffer);
+ return size;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return contractEncoder.decode(dataSlice.getInputStream());
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicArrayFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicArrayFieldEncoder.java
new file mode 100644
index 00000000..06d8b987
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicArrayFieldEncoder.java
@@ -0,0 +1,76 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+
+import com.jd.blockchain.binaryproto.BinarySliceSpec;
+import com.jd.blockchain.binaryproto.FieldSpec;
+import com.jd.blockchain.utils.io.BytesInputStream;
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesSlices;
+import com.jd.blockchain.utils.io.DynamicBytesSliceArray;
+import com.jd.blockchain.utils.io.NumberMask;
+
+public class DynamicArrayFieldEncoder extends AbstractFieldEncoder {
+
+ private DynamicValueConverter valueConverter;
+
+ public DynamicArrayFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader,
+ DynamicValueConverter valueConverter) {
+ super(sliceSpec, fieldSpec, reader);
+ this.valueConverter = valueConverter;
+ }
+
+ @Override
+ public int encode(Object dataContract, BytesOutputBuffer buffer) {
+ int size = 0;
+ Object[] values = readArrayValue(dataContract);
+ size += encodeArrayDynamic(values, buffer);
+
+ return size;
+ }
+
+ /**
+ * 对数组类型的值进行非固定长度的编码;
+ *
+ * @param values
+ * @param buffer
+ * @return
+ */
+ private int encodeArrayDynamic(Object[] values, BytesOutputBuffer buffer) {
+ int size = 0;
+
+ int count = values == null ? 0 : values.length;
+ byte[] countBytes = NumberMask.NORMAL.generateMask(count);
+ buffer.write(countBytes);
+ size += countBytes.length;
+
+ for (int i = 0; i < count; i++) {
+ size += valueConverter.encodeDynamicValue(values[i], buffer);
+ }
+
+ return size;
+ }
+
+ @Override
+ public BytesSlices decode(BytesInputStream bytesStream) {
+ return DynamicBytesSliceArray.resolve(bytesStream);
+ }
+
+ @Override
+ public Object decodeField(BytesSlices fieldBytes) {
+ Object[] values = (Object[]) Array.newInstance(valueConverter.getValueType(), fieldBytes.getCount());
+ BytesSlice itemSlice;
+ for (int i = 0; i < values.length; i++) {
+ itemSlice = fieldBytes.getDataSlice(i);
+ if (itemSlice.getSize() == 0) {
+ values[i] = valueConverter.getDefaultValue();
+ } else {
+ values[i] = valueConverter.decodeValue(itemSlice);
+ }
+ }
+ return values;
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicDataContract.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicDataContract.java
new file mode 100644
index 00000000..01240304
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicDataContract.java
@@ -0,0 +1,86 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import com.jd.blockchain.utils.io.BytesInputStream;
+import com.jd.blockchain.utils.io.BytesSlices;
+
+class DynamicDataContract implements InvocationHandler {
+
+ public static Method METHOD_GET_CLASS;
+
+ static {
+ try {
+ METHOD_GET_CLASS = Object.class.getMethod("getClass");
+ } catch (NoSuchMethodException | SecurityException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ private DataContractEncoderImpl contractEncoder;
+
+// private BytesSlice contractBytes;
+
+ // 字段的数据片段列表,首个是 HeaderSlice,其次是按字段顺序排列的数据片段;
+ private BytesSlices[] dataSlices;
+
+ private DynamicDataContract(BytesInputStream bytesStream, DataContractEncoderImpl contractEncoder) {
+ this.contractEncoder = contractEncoder;
+
+ init(bytesStream);
+ }
+
+ private void init(BytesInputStream bytesStream) {
+ // 解析出所有的数据片段;
+ dataSlices = new BytesSlices[contractEncoder.getFieldCount() + 1];
+
+ dataSlices[0] = contractEncoder.getHeaderEncoder().decode(bytesStream);
+
+ for (int i = 1; i < dataSlices.length; i++) {
+ dataSlices[i] = contractEncoder.getFieldEncoder(i - 1).decode(bytesStream);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T createContract(BytesInputStream bytesStream, DataContractEncoderImpl contractEncoder) {
+ return (T) Proxy.newProxyInstance(contractEncoder.getContractType().getClassLoader(),
+ contractEncoder.getContractTypeAsArray(), new DynamicDataContract(bytesStream, contractEncoder));
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T createContract(byte[] contractBytes, DataContractEncoderImpl contractEncoder) {
+ return (T) Proxy.newProxyInstance(contractEncoder.getContractType().getClassLoader(),
+ contractEncoder.getContractTypeAsArray(),
+ new DynamicDataContract(new BytesInputStream(contractBytes, 0, contractBytes.length), contractEncoder));
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ int fieldId = contractEncoder.getFieldId(method);
+ if (fieldId > -1) {
+ FieldEncoder encoder = contractEncoder.getFieldEncoder(fieldId);
+ return encoder.decodeField(dataSlices[fieldId + 1]);
+ }
+ if (METHOD_GET_CLASS == method) {
+ return contractEncoder.getContractType();
+ }
+ // invoke method declared in type Object;
+ Object result;
+ try {
+ //for some special case, interface's method without annotation
+ result = method.invoke(this, args);
+ } catch (Exception e) {
+ if (method.getReturnType().isPrimitive()) {
+ result = 0;
+ }
+ else {
+ result = null;
+ }
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicFieldEncoder.java
new file mode 100644
index 00000000..ea70b10c
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicFieldEncoder.java
@@ -0,0 +1,45 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.lang.reflect.Method;
+
+import com.jd.blockchain.binaryproto.BinarySliceSpec;
+import com.jd.blockchain.binaryproto.FieldSpec;
+import com.jd.blockchain.utils.io.BytesInputStream;
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlices;
+import com.jd.blockchain.utils.io.SingleBytesSliceArray;
+
+public class DynamicFieldEncoder extends AbstractFieldEncoder {
+
+ private DynamicValueConverter valueConverter;
+
+ public DynamicFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader,
+ DynamicValueConverter valueConverter) {
+ super(sliceSpec, fieldSpec, reader);
+ this.valueConverter = valueConverter;
+ }
+
+ @Override
+ public int encode(Object dataContract, BytesOutputBuffer buffer) {
+ int size = 0;
+ Object value = readValue(dataContract);
+ size += valueConverter.encodeDynamicValue(value, buffer);
+
+ return size;
+ }
+
+ @Override
+ public BytesSlices decode(BytesInputStream bytesStream) {
+ return SingleBytesSliceArray.resolveDynamic(bytesStream);
+ }
+
+ @Override
+ public Object decodeField(BytesSlices fieldBytes) {
+ // 非数组的字段,最多只有一个数据片段;
+ if (fieldBytes.getCount() == 0) {
+ return valueConverter.getDefaultValue();
+ }
+ return valueConverter.decodeValue(fieldBytes.getDataSlice(0));
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicValueConverter.java
new file mode 100644
index 00000000..81d18c63
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicValueConverter.java
@@ -0,0 +1,18 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlice;
+
+public interface DynamicValueConverter extends ValueConverter {
+
+ /**
+ * 写入一个动态长度的值;以一个头部的长度字节开始
+ * @param value
+ * @param buffer
+ * @return
+ */
+ int encodeDynamicValue(Object value, BytesOutputBuffer buffer);
+
+ Object decodeValue(BytesSlice dataSlice);
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/EnumSpecificationInfo.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/EnumSpecificationInfo.java
new file mode 100644
index 00000000..a93d064d
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/EnumSpecificationInfo.java
@@ -0,0 +1,127 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import com.jd.blockchain.binaryproto.EnumSpecification;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * Created by zhangshuang3 on 2018/6/21.
+ */
+public class EnumSpecificationInfo implements EnumSpecification {
+
+ private ValueType valueType;
+
+ private Class> dataType;
+
+ private int code;
+ private long version;
+ private String name;
+ private String description;
+
+ private Set items = new LinkedHashSet<>();
+ // private Map itemCodeMapping = new HashMap<>();
+ // private Map codeItemMapping = new HashMap<>();
+
+ public EnumSpecificationInfo(ValueType valueType, int code, long version, String name, String description, Class> dataType) {
+ this.valueType = valueType;
+ this.code = code;
+ this.version = version;
+ this.name = name;
+ this.description = description;
+ this.dataType = dataType;
+ }
+
+ @Override
+ public int getCode() {
+ return this.code;
+ }
+
+ @Override
+ public long getVersion() {
+ return this.version;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public String getDescription() {
+ return this.description;
+ }
+
+ @Override
+ public ValueType getValueType() {
+ return this.valueType;
+ }
+
+ @Override
+ public int[] getItemValues() {
+ int[] values = new int[items.size()];
+ int i = 0;
+ for (EnumConstant it : items) {
+ values[i] = it.code;
+ i++;
+ }
+ return values;
+ }
+
+ @Override
+ public String[] getItemNames() {
+ String[] names = new String[items.size()];
+ int i = 0;
+ for (EnumConstant it : items) {
+ names[i] = it.name;
+ i++;
+ }
+ return names;
+ }
+
+ public Class> getDataType() {
+ return dataType;
+ }
+
+ public Object[] getConstants() {
+ Object[] constants = new Object[items.size()];
+ int i = 0;
+ for (EnumConstant it : items) {
+ constants[i] = it.constant;
+ i++;
+ }
+ return constants;
+ }
+
+ public void addConstant(EnumConstant item) {
+ items.add(item);
+ }
+
+ public static class EnumConstant {
+
+ private int code;
+
+ private String name;
+
+ private Object constant;
+
+ public int getCode() {
+ return code;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Object getConstant() {
+ return constant;
+ }
+
+ public EnumConstant(int code, String name, Object constant) {
+ this.code = code;
+ this.name = name;
+ this.constant = constant;
+ }
+ }
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/EnumValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/EnumValueConverter.java
new file mode 100644
index 00000000..b3c621aa
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/EnumValueConverter.java
@@ -0,0 +1,103 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.binaryproto.DataContractException;
+import com.jd.blockchain.utils.ValueType;
+import com.jd.blockchain.utils.io.BytesSlice;
+
+public class EnumValueConverter implements FixedValueConverter {
+
+ private Class> enumType;
+
+ private ValueType codeType;
+
+ private int[] values;
+
+ private Object[] constants;
+
+ private FixedValueConverter valueConverter;
+
+ public EnumValueConverter(Class> enumType, ValueType codeType, int[] values, Object[] constants, FixedValueConverter valueConverter) {
+ this.enumType = enumType;
+ this.values = values;
+ this.constants = constants;
+ this.valueConverter = valueConverter;
+ this.codeType = codeType;
+
+ }
+
+ @Override
+ public Class> getValueType() {
+ return enumType;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return null;
+ }
+
+ //lookup CODE value
+ private Object getEnumCode(Object value, int[] codes, Object[] constants) {
+ int codeIndex = 0;
+
+ for (int i = 0; i < constants.length; i++) {
+ if (value.toString().equals(constants[i].toString())) {
+ codeIndex = i;
+ break;
+ }
+ }
+ switch (codeType) {
+ case INT8:
+ return (byte)codes[codeIndex];
+ case INT16:
+ return (short)codes[codeIndex];
+ case INT32:
+ return codes[codeIndex];
+ default:
+ throw new DataContractException(String.format("Enum code error!"));
+ }
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ // 注:由于是通过反射调用的,已经在外围做了检查,此处不需要重复检查枚举值的范围;
+ //首先把枚举常量转换成对应的CODE
+ Object code = getEnumCode(value, values, constants);
+ return valueConverter.encodeValue(code, buffer, offset);
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+
+ Object v = valueConverter.decodeValue(dataSlice);
+ switch (codeType) {
+ case INT8:
+ for (int i = 0; i < values.length; i++) {
+ if ((byte)values[i] == (byte)v) {
+ return constants[i];
+ }
+ }
+ throw new DataContractException(String.format(
+ "The decoding value is out of all enum constants! --[value=%s][enum type=%s]", v, enumType.toString()));
+ case INT16:
+ for (int i = 0; i < values.length; i++) {
+ if ((short)values[i] == (short)v) {
+ return constants[i];
+ }
+ }
+ throw new DataContractException(String.format(
+ "The decoding value is out of all enum constants! --[value=%s][enum type=%s]", v, enumType.toString()));
+ case INT32:
+ for (int i = 0; i < values.length; i++) {
+ if ((int)values[i] == (int)v) {
+ return constants[i];
+ }
+ }
+ throw new DataContractException(String.format(
+ "The decoding value is out of all enum constants! --[value=%s][enum type=%s]", v, enumType.toString()));
+ default:
+ throw new DataContractException(String.format("Enum code error!"));
+
+ }
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FieldEncoder.java
new file mode 100644
index 00000000..404b9334
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FieldEncoder.java
@@ -0,0 +1,16 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.lang.reflect.Method;
+
+import com.jd.blockchain.binaryproto.FieldSpec;
+import com.jd.blockchain.utils.io.BytesSlices;
+
+public interface FieldEncoder extends SliceEncoder {
+
+ Method getReader();
+
+ FieldSpec getFieldSpecification();
+
+ Object decodeField(BytesSlices fieldBytes);
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FieldSpecInfo.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FieldSpecInfo.java
new file mode 100644
index 00000000..f682a57b
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FieldSpecInfo.java
@@ -0,0 +1,121 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.binaryproto.DataSpecification;
+import com.jd.blockchain.binaryproto.EnumSpecification;
+import com.jd.blockchain.binaryproto.FieldSpec;
+import com.jd.blockchain.utils.ValueType;
+
+public class FieldSpecInfo implements FieldSpec {
+
+ private int order;
+
+ private String name;
+
+ private String description;
+
+ private boolean repeatable;
+
+ private ValueType primitiveType;
+
+ private EnumSpecification enumSpec;
+
+ private DataSpecification contractTypeSpec;
+
+ private int maxSize;
+
+ private Class> dataType;
+
+ private boolean isGenericContract = false;
+
+ public FieldSpecInfo(int order, String name, String decription, ValueType primitiveType, boolean repeatable,
+ int maxSize, Class> dataType) {
+ if (primitiveType == null) {
+ throw new IllegalArgumentException("primitiveType is null!");
+ }
+ this.order = order;
+ this.name = name;
+ this.description = decription;
+ this.primitiveType = primitiveType;
+ this.repeatable = repeatable;
+ this.maxSize = maxSize;
+ this.dataType = dataType;
+ }
+
+ public FieldSpecInfo(int order, String name, String decription, EnumSpecification enumSpec, boolean repeatable,
+ Class> enumType) {
+ if (enumSpec == null) {
+ throw new IllegalArgumentException("enum specification is null!");
+ }
+ this.order = order;
+ this.name = name;
+ this.description = decription;
+ this.enumSpec = enumSpec;
+ this.repeatable = repeatable;
+ this.maxSize = -1;
+ this.dataType = enumType;
+ }
+
+ public FieldSpecInfo(int order, String name, String decription, DataSpecification contractTypeSpec,
+ boolean repeatable, Class> contractType, boolean isGenericContract) {
+ if (contractTypeSpec == null) {
+ throw new IllegalArgumentException("contractType is null!");
+ }
+ this.order = order;
+ this.name = name;
+ this.description = decription;
+ this.contractTypeSpec = contractTypeSpec;
+ this.repeatable = repeatable;
+ this.maxSize = -1;
+ this.dataType = contractType;
+ this.isGenericContract = isGenericContract;
+ }
+
+ @Override
+ public ValueType getPrimitiveType() {
+ return primitiveType;
+ }
+
+ @Override
+ public EnumSpecification getRefEnum() {
+ return enumSpec;
+ }
+
+ @Override
+ public DataSpecification getRefContract() {
+ return contractTypeSpec;
+ }
+
+ @Override
+ public boolean isRepeatable() {
+ return repeatable;
+ }
+
+ @Override
+ public int getMaxSize() {
+ return maxSize;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public boolean isGenericContract() {
+ return isGenericContract;
+ }
+
+ public int getOrder() {
+ return order;
+ }
+
+ public Class> getDataType() {
+ return dataType;
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedArrayFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedArrayFieldEncoder.java
new file mode 100644
index 00000000..43341d2b
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedArrayFieldEncoder.java
@@ -0,0 +1,79 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+
+import com.jd.blockchain.binaryproto.BinarySliceSpec;
+import com.jd.blockchain.binaryproto.FieldSpec;
+import com.jd.blockchain.utils.io.BytesInputStream;
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesSlices;
+import com.jd.blockchain.utils.io.FixedBytesSliceArray;
+import com.jd.blockchain.utils.io.NumberMask;
+
+public class FixedArrayFieldEncoder extends AbstractFieldEncoder {
+
+ private FixedValueConverter valueConverter;
+
+ public FixedArrayFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader, FixedValueConverter valueConverter) {
+ super(sliceSpec, fieldSpec, reader);
+ this.valueConverter = valueConverter;
+ }
+
+ @Override
+ public int encode(Object dataContract, BytesOutputBuffer buffer) {
+ int size = 0;
+
+ Object[] values = readArrayValue(dataContract);
+ size += encodeArray(values, buffer);
+
+ return size;
+ }
+
+ /**
+ * 对数组类型的值进行固定长度的编码;
+ *
+ * @param values
+ * @param buffer
+ * @return
+ */
+ private int encodeArray(Object[] values, BytesOutputBuffer buffer) {
+ int count = values == null ? 0 : values.length;
+
+ int counterSize = NumberMask.NORMAL.getMaskLength(count);
+ int elementSize = sliceSpec.getLength();
+
+ int size = counterSize + elementSize * count;
+ byte[] outbuff = new byte[size];
+ NumberMask.NORMAL.writeMask(count, outbuff, 0);
+
+ for (int i = 0; i < count; i++) {
+ valueConverter.encodeValue(values[i], outbuff, counterSize + elementSize * i);
+ }
+
+ buffer.write(outbuff);
+ return size;
+ }
+
+ @Override
+ public BytesSlices decode(BytesInputStream bytesStream) {
+ return FixedBytesSliceArray.resolve(bytesStream, sliceSpec.getLength());
+ }
+
+ @Override
+ public Object decodeField(BytesSlices fieldBytes) {
+ Object[] values = (Object[]) Array.newInstance(valueConverter.getValueType(), fieldBytes.getCount());
+ BytesSlice itemSlice;
+ for (int i = 0; i < values.length; i++) {
+ itemSlice = fieldBytes.getDataSlice(i);
+ if (itemSlice.getSize() == 0) {
+ values[i] = valueConverter.getDefaultValue();
+ } else {
+ values[i] = valueConverter.decodeValue(itemSlice);
+ }
+ }
+ return values;
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedFieldEncoder.java
new file mode 100644
index 00000000..d1d8d9d6
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedFieldEncoder.java
@@ -0,0 +1,58 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import java.lang.reflect.Method;
+
+import com.jd.blockchain.binaryproto.BinarySliceSpec;
+import com.jd.blockchain.binaryproto.FieldSpec;
+import com.jd.blockchain.utils.io.BytesInputStream;
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlices;
+import com.jd.blockchain.utils.io.SingleBytesSliceArray;
+
+public class FixedFieldEncoder extends AbstractFieldEncoder {
+
+ private FixedValueConverter valueConverter;
+
+ public FixedFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader, FixedValueConverter valueConverter) {
+ super(sliceSpec, fieldSpec, reader);
+ this.valueConverter = valueConverter;
+ }
+
+ @Override
+ public int encode(Object dataContract, BytesOutputBuffer buffer) {
+ int size = 0;
+ Object value = readValue(dataContract);
+ size += encodeValue(value, buffer);
+
+ return size;
+ }
+
+ /**
+ * 把固定长度的值序列化到指定的缓冲区;
+ *
+ * @param value
+ * @param buffer
+ * @return
+ */
+ private int encodeValue(Object value, BytesOutputBuffer buffer) {
+ byte[] valueBytes = new byte[sliceSpec.getLength()];
+ int size = valueConverter.encodeValue(value, valueBytes, 0);
+ buffer.write(valueBytes);
+ return size;
+ }
+
+ @Override
+ public BytesSlices decode(BytesInputStream bytesStream) {
+ return SingleBytesSliceArray.create(bytesStream, sliceSpec.getLength());
+ }
+
+ @Override
+ public Object decodeField(BytesSlices fieldBytes) {
+ // 非数组的字段,最多只有一个数据片段;
+ if (fieldBytes.getCount() == 0) {
+ return valueConverter.getDefaultValue();
+ }
+ return valueConverter.decodeValue(fieldBytes.getDataSlice(0));
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedValueConverter.java
new file mode 100644
index 00000000..2a39e2f8
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedValueConverter.java
@@ -0,0 +1,25 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+
+public interface FixedValueConverter extends ValueConverter {
+
+ /**
+ * 将把固定长度的值序列化到指定的缓冲区;
+ *
+ *
+ * 注:实现者应用确保写入的范围不要越界(超出 {@link #getSliceSpecification()} 属性指定的长度);
+ *
+ * @param value
+ * 要序列化的值;
+ * @param buffer
+ * 保存结果的缓冲区;
+ * @param offset
+ * 缓冲区的写入起始位置;
+ * @return 返回写入的长度;
+ */
+ int encodeValue(Object value, byte[] buffer, int offset);
+
+ Object decodeValue(BytesSlice dataSlice);
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/GenericFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/GenericFieldEncoder.java
new file mode 100644
index 00000000..1f856e65
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/GenericFieldEncoder.java
@@ -0,0 +1,199 @@
+//package com.jd.blockchain.binaryproto.impl2;
+//
+//import java.lang.reflect.Array;
+//import java.lang.reflect.InvocationTargetException;
+//import java.lang.reflect.Method;
+//
+//import com.jd.blockchain.binaryproto.BinarySliceSpec;
+//import com.jd.blockchain.binaryproto.FieldSpec;
+//
+//import my.utils.io.BytesInputStream;
+//import my.utils.io.BytesOutputBuffer;
+//import my.utils.io.BytesSlice;
+//import my.utils.io.BytesSlices;
+//import my.utils.io.DynamicBytesSliceArray;
+//import my.utils.io.FixedBytesSliceArray;
+//import my.utils.io.NumberMask;
+//import my.utils.io.SingleBytesSliceArray;
+//
+//public abstract class GenericFieldEncoder implements FieldEncoder {
+//
+// protected BinarySliceSpec sliceSpec;
+//
+// protected FieldSpec fieldSpec;
+//
+// protected Method reader;
+//
+// public GenericFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader) {
+// this.sliceSpec = sliceSpec;
+// this.fieldSpec = fieldSpec;
+// this.reader = reader;
+// }
+//
+// @Override
+// public BinarySliceSpec getSliceSpecification() {
+// return sliceSpec;
+// }
+//
+// @Override
+// public FieldSpec getFieldSpecification() {
+// return fieldSpec;
+// }
+//
+// @Override
+// public Method getReader() {
+// return reader;
+// }
+//
+// @Override
+// public int encode(Object dataContract, BytesOutputBuffer buffer) {
+// int size = 0;
+// if (sliceSpec.isRepeatable()) {
+// Object[] values = readArrayValue(dataContract);
+// if (sliceSpec.isDynamic()) {
+// size += encodeArrayDynamic(values, buffer);
+// } else {
+// size += encodeArray(values, buffer);
+// }
+// } else {
+// Object value = readValue(dataContract);
+// if (sliceSpec.isDynamic()) {
+// size += encodeDynamicValue(value, buffer);
+// } else {
+// size += encodeValue(value, buffer);
+// }
+// }
+//
+// return size;
+// }
+//
+// /**
+// * 把固定长度的值序列化到指定的缓冲区;
+// *
+// * @param value
+// * @param buffer
+// * @return
+// */
+// private int encodeValue(Object value, BytesOutputBuffer buffer) {
+// byte[] valueBytes = new byte[sliceSpec.getLength()];
+// return encodeValue(value, valueBytes, 0);
+// }
+//
+// /**
+// * 将把固定长度的值序列化到指定的缓冲区;
+// *
+// *
+// * 注:实现者应用确保写入的范围不要越界(超出 {@link #getSliceSpecification()} 属性指定的长度);
+// *
+// * @param value
+// * 要序列化的值;
+// * @param buffer
+// * 保存结果的缓冲区;
+// * @param offset
+// * 缓冲区的写入起始位置;
+// * @return 返回写入的长度;
+// */
+// abstract int encodeValue(Object value, byte[] buffer, int offset);
+//
+// abstract int encodeDynamicValue(Object value, BytesOutputBuffer buffer);
+//
+// /**
+// * 对数组类型的值进行固定长度的编码;
+// *
+// * @param values
+// * @param buffer
+// * @return
+// */
+// private int encodeArray(Object[] values, BytesOutputBuffer buffer) {
+// int count = values == null ? 0 : values.length;
+//
+// int counterSize = NumberMask.NORMAL.getMaskLength(count);
+// int elementSize = sliceSpec.getLength();
+//
+// int size = counterSize + elementSize * count;
+// byte[] outbuff = new byte[size];
+// NumberMask.NORMAL.writeMask(count, outbuff, 0);
+//
+// for (int i = 0; i < count; i++) {
+// encodeValue(values[i], outbuff, counterSize + elementSize * i);
+// }
+//
+// buffer.write(outbuff);
+// return size;
+// }
+//
+// /**
+// * 对数组类型的值进行非固定长度的编码;
+// *
+// * @param values
+// * @param buffer
+// * @return
+// */
+// private int encodeArrayDynamic(Object[] values, BytesOutputBuffer buffer) {
+// int size = 0;
+//
+// int count = values == null ? 0 : values.length;
+// byte[] countBytes = NumberMask.NORMAL.generateMask(count);
+// buffer.write(countBytes);
+// size += countBytes.length;
+//
+// for (int i = 0; i < count; i++) {
+// size += encodeDynamicValue(values[i], buffer);
+// }
+//
+// return size;
+// }
+//
+// private Object readValue(Object dataContract) {
+// try {
+// return reader.invoke(dataContract);
+// } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+// throw new IllegalStateException(e.getMessage(), e);
+// }
+// }
+//
+// private Object[] readArrayValue(Object dataContract) {
+// return (Object[]) readValue(dataContract);
+// }
+//
+// @Override
+// public BytesSlices decode(BytesInputStream bytesStream) {
+// if (sliceSpec.isRepeatable()) {
+// if (sliceSpec.isDynamic()) {
+// return DynamicBytesSliceArray.resolve(bytesStream);
+// }else {
+// return FixedBytesSliceArray.resolve(bytesStream, sliceSpec.getLength());
+// }
+// }else {
+// if (sliceSpec.isDynamic()) {
+// return SingleBytesSliceArray.resolveDynamic(bytesStream);
+// }else {
+// return SingleBytesSliceArray.create(bytesStream, sliceSpec.getLength());
+// }
+// }
+// }
+//
+// @Override
+// public Object decodeField(BytesSlices fieldBytes) {
+// if (sliceSpec.isRepeatable()) {
+// Object[] values = (Object[]) Array.newInstance(getFieldType(), fieldBytes.getCount());
+// for (int i = 0; i < values.length; i++) {
+// values[i] = decodeValue(fieldBytes.getDataSlice(i));
+// }
+// return values;
+// }
+// //非数组的字段,最多只有一个数据片段;
+// if (fieldBytes.getCount() == 0) {
+// return getNullValue();
+// }
+// return decodeValue(fieldBytes.getDataSlice(0));
+// }
+//
+//
+// abstract Class> getFieldType();
+//
+// abstract Object getNullValue();
+//
+// abstract Object decodeValue(BytesSlice dataSlice);
+//
+//}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/HeaderEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/HeaderEncoder.java
new file mode 100644
index 00000000..a1fe4c36
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/HeaderEncoder.java
@@ -0,0 +1,102 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.binaryproto.BinarySliceSpec;
+import com.jd.blockchain.utils.io.BytesInputStream;
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesSliceArrayWrapper;
+import com.jd.blockchain.utils.io.BytesSlices;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+/**
+ * 分段编码器;
+ *
+ * @author huanghaiquan
+ *
+ */
+public class HeaderEncoder implements SliceEncoder {
+
+ public static final int HEAD_BYTES = 12;
+
+ private BinarySliceSpec sliceSpec;
+
+ private int code;
+
+ private long version;
+
+ private String name;
+
+ private String description;
+
+ public int getCode() {
+ return code;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public HeaderEncoder(BinarySliceSpec sliceSpec, int code, long version, String name, String description) {
+ this.sliceSpec = sliceSpec;
+ this.code = code;
+ this.version = version;
+ this.name = name;
+ this.description = description;
+ }
+
+ /**
+ * 校验指定的数据契约字节列表的头部是否一致;
+ *
+ * @param dataContractBytes
+ * @return
+ */
+ boolean verifyHeaders(BytesSlice dataContractBytes) {
+ int code = resolveCode(dataContractBytes);
+ if (code != this.code) {
+ return false;
+ }
+ long version = resolveVersion(dataContractBytes);
+ return version == this.version;
+ }
+
+ public static int resolveCode(BytesSlice dataContractBytes) {
+ return dataContractBytes.getInt();
+ }
+
+ public static long resolveVersion(BytesSlice dataContractBytes) {
+ return dataContractBytes.getLong(4);
+ }
+
+ @Override
+ public BinarySliceSpec getSliceSpecification() {
+ return sliceSpec;
+ }
+
+ @Override
+ public int encode(Object dataContract, BytesOutputBuffer buffer) {
+ byte[] headBytes = new byte[HEAD_BYTES];
+ BytesUtils.toBytes(code, headBytes);
+ BytesUtils.toBytes(version, headBytes, 4);
+ buffer.write(headBytes);
+ return HEAD_BYTES;
+ }
+
+ @Override
+ public BytesSlices decode(BytesInputStream bytesStream) {
+ return BytesSliceArrayWrapper.wrap(bytesStream.readSlice(HEAD_BYTES));
+ }
+
+ // @Override
+ // public BytesSlices decode(byte[] dataContractBytes, int offset) {
+ // return SingleBytesSliceArray.create(dataContractBytes, offset, HEAD_BYTES);
+ // }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16CharConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16CharConverter.java
new file mode 100644
index 00000000..f026ea48
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16CharConverter.java
@@ -0,0 +1,29 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public class Int16CharConverter implements FixedValueConverter{
+
+ @Override
+ public Class> getValueType() {
+ return char.class;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return '\u0000';
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ BytesUtils.toBytes((char)value, buffer, offset);
+ return 2;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getChar();
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16CharWrapperConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16CharWrapperConverter.java
new file mode 100644
index 00000000..10707ff2
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16CharWrapperConverter.java
@@ -0,0 +1,29 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public class Int16CharWrapperConverter implements FixedValueConverter{
+
+ @Override
+ public Class> getValueType() {
+ return Character.class;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return null;
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ BytesUtils.toBytes((char)value, buffer, offset);
+ return 2;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getChar();
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16ShortConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16ShortConverter.java
new file mode 100644
index 00000000..e7572687
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16ShortConverter.java
@@ -0,0 +1,29 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public class Int16ShortConverter implements FixedValueConverter{
+
+ @Override
+ public Class> getValueType() {
+ return short.class;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return 0;
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ BytesUtils.toBytes((short)value, buffer, offset);
+ return 2;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getShort();
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16ShortWrapperConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16ShortWrapperConverter.java
new file mode 100644
index 00000000..b7861c45
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16ShortWrapperConverter.java
@@ -0,0 +1,29 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public class Int16ShortWrapperConverter implements FixedValueConverter{
+
+ @Override
+ public Class> getValueType() {
+ return Short.class;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return null;
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ BytesUtils.toBytes((short)value, buffer, offset);
+ return 2;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getShort();
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int32IntConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int32IntConverter.java
new file mode 100644
index 00000000..79356f2a
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int32IntConverter.java
@@ -0,0 +1,29 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public class Int32IntConverter implements FixedValueConverter{
+
+ @Override
+ public Class> getValueType() {
+ return int.class;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return 0;
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ BytesUtils.toBytes((int)value, buffer, offset);
+ return 4;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getInt();
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int32IntWrapperConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int32IntWrapperConverter.java
new file mode 100644
index 00000000..2dbe3bb2
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int32IntWrapperConverter.java
@@ -0,0 +1,29 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public class Int32IntWrapperConverter implements FixedValueConverter{
+
+ @Override
+ public Class> getValueType() {
+ return Integer.class;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return null;
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ BytesUtils.toBytes((int)value, buffer, offset);
+ return 4;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getInt();
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int64LongConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int64LongConverter.java
new file mode 100644
index 00000000..9810ae73
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int64LongConverter.java
@@ -0,0 +1,29 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public class Int64LongConverter implements FixedValueConverter{
+
+ @Override
+ public Class> getValueType() {
+ return Long.class;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return 0;
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ BytesUtils.toBytes((long)value, buffer, offset);
+ return 8;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getLong();
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int64LongWrapperConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int64LongWrapperConverter.java
new file mode 100644
index 00000000..ebf09087
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int64LongWrapperConverter.java
@@ -0,0 +1,29 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public class Int64LongWrapperConverter implements FixedValueConverter{
+
+ @Override
+ public Class> getValueType() {
+ return Long.class;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return null;
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ BytesUtils.toBytes((long)value, buffer, offset);
+ return 8;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getLong();
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int8ByteConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int8ByteConverter.java
new file mode 100644
index 00000000..d38b7afc
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int8ByteConverter.java
@@ -0,0 +1,28 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+
+public class Int8ByteConverter implements FixedValueConverter{
+
+ @Override
+ public Class> getValueType() {
+ return byte.class;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return 0;
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ buffer[offset] = (byte)value;
+ return 1;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getByte();
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int8ByteWrapperConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int8ByteWrapperConverter.java
new file mode 100644
index 00000000..d8cf510b
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int8ByteWrapperConverter.java
@@ -0,0 +1,28 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesSlice;
+
+public class Int8ByteWrapperConverter implements FixedValueConverter{
+
+ @Override
+ public Class> getValueType() {
+ return Byte.class;
+ }
+
+ @Override
+ public Object getDefaultValue() {
+ return null;
+ }
+
+ @Override
+ public int encodeValue(Object value, byte[] buffer, int offset) {
+ buffer[offset] = (byte)value;
+ return 1;
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getByte();
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/RepeatableFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/RepeatableFieldEncoder.java
new file mode 100644
index 00000000..fbf15a47
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/RepeatableFieldEncoder.java
@@ -0,0 +1,31 @@
+//package com.jd.blockchain.binaryproto.impl2;
+//
+//import java.lang.reflect.Method;
+//
+//import com.jd.blockchain.binaryproto.BinarySliceSpec;
+//import com.jd.blockchain.binaryproto.FieldSpec;
+//
+//import my.utils.io.BytesOutputBuffer;
+//
+//public class RepeatableFieldEncoder extends AbstractFieldEncoder {
+//
+//
+// public RepeatableFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader) {
+// super(sliceSpec, fieldSpec, reader);
+// }
+//
+// @Override
+// public Object decodeValue(byte[] dataContractBytes, int offset) {
+// // TODO Auto-generated method stub
+// return null;
+// }
+//
+// @Override
+// public int encode(Object dataContract, BytesOutputBuffer buffer) {
+// // TODO Auto-generated method stub
+// return 0;
+// }
+//
+//
+//
+//}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/SliceEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/SliceEncoder.java
new file mode 100644
index 00000000..1f87e0f2
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/SliceEncoder.java
@@ -0,0 +1,55 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.binaryproto.BinarySliceSpec;
+import com.jd.blockchain.utils.io.BytesInputStream;
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlices;
+
+/**
+ * 分段编码器;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface SliceEncoder {
+
+ BinarySliceSpec getSliceSpecification();
+
+ /**
+ * 将此编码器表示的数据契约分段输出的指定的缓冲区;
+ *
+ * @param dataContract
+ * 数据契约的实例;当前编码器从中读取分段的值;
+ * @param buffer
+ * 要写入的缓冲区;调用者需要确保
+ * @param offset
+ * 缓冲区的写入起始位置;
+ * @return 写入的字节数;
+ */
+ int encode(Object dataContract, BytesOutputBuffer buffer);
+
+// /**
+// * 从指定的数据契约的数据中读取当前编码器表示的分段的数据;
+// *
+// * @param dataContractBytes
+// * @return
+// */
+// BytesSlices decode(BytesSlice dataContractBytes, int offset);
+
+ /**
+ * 从指定的数据契约的数据中读取当前编码器表示的分段的数据;
+ *
+ * @param dataContractBytes
+ * @return
+ */
+ BytesSlices decode(BytesInputStream bytesStream);
+
+// /**
+// * 从指定的数据契约的数据中读取当前编码器表示的分段的数据;
+// *
+// * @param dataContractBytes
+// * @return
+// */
+// BytesSlices decode(byte[] dataContractBytes, int offset);
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/StringValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/StringValueConverter.java
new file mode 100644
index 00000000..eaf96a21
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/StringValueConverter.java
@@ -0,0 +1,27 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+import com.jd.blockchain.utils.io.BytesOutputBuffer;
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public class StringValueConverter extends AbstractDynamicValueConverter {
+
+ public StringValueConverter() {
+ super(String.class);
+ }
+
+ @Override
+ public Object decodeValue(BytesSlice dataSlice) {
+ return dataSlice.getString();
+ }
+
+ @Override
+ public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) {
+ byte[] bytes = value == null ? BytesUtils.EMPTY_BYTES : BytesUtils.toBytes((String) value);
+ int size = bytes.length;
+ size += writeSize(bytes.length, buffer);
+ buffer.write(bytes);
+ return size;
+ }
+
+}
diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/ValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/ValueConverter.java
new file mode 100644
index 00000000..74bf0a00
--- /dev/null
+++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/ValueConverter.java
@@ -0,0 +1,14 @@
+package com.jd.blockchain.binaryproto.impl2;
+
+public interface ValueConverter {
+
+ Class> getValueType();
+
+ /**
+ * 返回类型的默认初始值;
+ *
+ * @return
+ */
+ Object getDefaultValue();
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/BinaryEncodingTest.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/BinaryEncodingTest.java
new file mode 100644
index 00000000..fba70646
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/BinaryEncodingTest.java
@@ -0,0 +1,547 @@
+package test.com.jd.blockchain.binaryproto;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.jd.blockchain.binaryproto.BinaryEncodingUtils;
+import com.jd.blockchain.binaryproto.DataContractException;
+import com.jd.blockchain.binaryproto.DataContractRegistry;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.io.BytesEncoding;
+import com.jd.blockchain.utils.io.BytesUtils;
+import com.jd.blockchain.utils.io.NumberMask;
+import com.jd.blockchain.utils.net.NetworkAddress;
+
+public class BinaryEncodingTest {
+
+ /**
+ * 此测试用例是对序列化过程的验证,包括:头部、基本类型的值(boolean、整数、字符串、字节数组、BytesSerializable)、字段顺序的正确性;
+ */
+ @Test
+ public void testEncoding_Header_OrderedPrimitiveFields() {
+ DataContractRegistry.register(PrimitiveDatas.class);
+ PrimitiveDatasImpl pd = new PrimitiveDatasImpl();
+ NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false);
+ pd.setId(123);
+ pd.setEnable(true);
+ pd.setBoy((byte) 10);
+ pd.setAge((short) 100);
+ pd.setName("John");
+ pd.setImage("Image of John".getBytes());
+ pd.setFlag('x');
+ pd.setValue(93239232);
+ pd.setConfig(Bytes.fromString("Configuration of something."));
+ pd.setNetworkAddress(networkAddress);
+
+ byte[] bytes = BinaryEncodingUtils.encode(pd, PrimitiveDatas.class);
+ int offset = 0;
+ int code = BytesUtils.toInt(bytes, offset);
+ offset += 12;
+ assertEquals(0x05, code);
+ offset = assertFieldEncoding(pd, bytes, offset);
+
+ // 到了结尾;
+ assertEquals(bytes.length, offset);
+ }
+
+ private int assertFieldEncoding(PrimitiveDatas pd, byte[] bytes, int offset) {
+
+ // Code 和 Version 的占据了 12 字节;
+ int id = BytesUtils.toInt(bytes, offset);
+ offset += 4;
+ assertEquals(pd.getId(), id);
+
+ byte enable = bytes[offset];
+ byte expEnable = pd.isEnable() ? (byte) 1 : (byte) 0;
+ offset += 1;
+ assertEquals(expEnable, enable);
+
+ byte boy = bytes[offset];
+ offset += 1;
+ assertEquals(pd.isBoy(), boy);
+
+ short age = BytesUtils.toShort(bytes, offset);
+ offset += 2;
+ assertEquals(pd.getAge(), age);
+
+ byte[] nameBytes = BytesEncoding.readInNormal(bytes, offset);
+ String name = BytesUtils.toString(nameBytes);
+ int maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]);
+ offset += nameBytes.length + maskLen;
+ assertEquals(pd.getName().length(), name.length());
+
+ long value = BytesUtils.toLong(bytes, offset);
+ offset += 8;
+ assertEquals(pd.getValue(), value);
+
+ byte[] image = BytesEncoding.readInNormal(bytes, offset);
+ maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]);
+ offset += image.length + maskLen;
+ assertTrue(BytesUtils.equals(pd.getImage(), image));
+
+ char flag = BytesUtils.toChar(bytes, offset);
+ offset += 2;
+ assertEquals(pd.getFlag(), flag);
+
+ byte[] config = BytesEncoding.readInNormal(bytes, offset);
+ maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]);
+ offset += config.length + maskLen;
+ assertTrue(BytesUtils.equals(pd.getConfig().toBytes(), config));
+
+ byte[] setting = BytesEncoding.readInNormal(bytes, offset);
+ assertEquals(0, setting.length);
+ maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]);
+ offset += setting.length + maskLen;
+
+ byte[] networkaddr = BytesEncoding.readInNormal(bytes, offset);
+ maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]);
+ offset += networkaddr.length + maskLen;
+ assertTrue(BytesUtils.equals(pd.getNetworkAddr().toBytes(), networkaddr));
+
+ return offset;
+ }
+
+ @Test
+ public void testEncoding_Null() {
+ DataContractRegistry.register(PrimitiveDatas.class);
+
+ byte[] bytes = BinaryEncodingUtils.encode(null, PrimitiveDatas.class);
+ int offset = 0;
+ int code = BytesUtils.toInt(bytes, offset);
+ offset += 12;// 暂不校验 version;
+ assertEquals(0x05, code);
+
+ // 到了结尾;
+ assertEquals(bytes.length, offset);
+ }
+
+ /**
+ * 此测试用例是对序列化过程的验证,包括:头部、基本类型的值(boolean、整数、字符串、字节数组、BytesSerializable)、字段顺序的正确性;
+ */
+ @Test
+ public void testDecoding_Header_OrderedPrimitiveFields() {
+ DataContractRegistry.register(PrimitiveDatas.class);
+ PrimitiveDatasImpl pd = new PrimitiveDatasImpl();
+ NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false);
+ pd.setId(123);
+ pd.setEnable(true);
+ pd.setBoy((byte) 10);
+ pd.setAge((short) 100);
+ pd.setName("John");
+ pd.setImage("Image of John".getBytes());
+ pd.setFlag('x');
+ pd.setValue(93239232);
+ pd.setConfig(Bytes.fromString("Configuration of something."));
+ pd.setNetworkAddress(networkAddress);
+
+ byte[] bytes = BinaryEncodingUtils.encode(pd, PrimitiveDatas.class);
+ PrimitiveDatas decodeData = BinaryEncodingUtils.decode(bytes);
+ assertEquals(pd.getId(), decodeData.getId());
+ assertEquals(pd.isEnable(), decodeData.isEnable());
+ assertEquals(pd.isBoy(), decodeData.isBoy());
+ assertEquals(pd.getAge(), decodeData.getAge());
+ assertEquals(pd.getName(), decodeData.getName());
+ assertTrue(BytesUtils.equals(pd.getImage(), decodeData.getImage()));
+ assertEquals(pd.getFlag(), decodeData.getFlag());
+ assertEquals(pd.getValue(), decodeData.getValue());
+ assertEquals(pd.getConfig(), decodeData.getConfig());
+ assertNull(decodeData.getSetting());
+ assertEquals(pd.getNetworkAddr().getHost(), decodeData.getNetworkAddr().getHost());
+ assertEquals(pd.getNetworkAddr().getPort(), decodeData.getNetworkAddr().getPort());
+ }
+
+ /**
+ * 此测试用例是对序列化过程的验证,包括:头部、枚举值、字段顺序的正确性;
+ */
+ @Test
+ public void testEncoding_Header_OrderedEnumFields() {
+ DataContractRegistry.register(EnumDatas.class);
+ EnumDatasImpl enumDatas = new EnumDatasImpl();
+ enumDatas.setLevel(EnumLevel.V1);
+
+ byte[] bytes = BinaryEncodingUtils.encode(enumDatas, EnumDatas.class);
+ int offset = 0;
+ int code = BytesUtils.toInt(bytes, offset);
+ offset += 12;
+ assertEquals(0x07, code);
+
+ byte enumCode = bytes[offset];
+ offset += 1;
+ assertEquals(enumDatas.getLevel().CODE, enumCode);
+
+ // 到了结尾;
+ assertEquals(bytes.length, offset);
+ }
+
+ /**
+ * 此测试用例是对反序列化过程的验证,包括:头部、枚举值、字段顺序的正确性;
+ */
+ @Test
+ public void testDecoding_Header_OrderedEnumFields() {
+ DataContractRegistry.register(EnumDatas.class);
+ EnumDatasImpl enumDatas = new EnumDatasImpl();
+ enumDatas.setLevel(EnumLevel.V1);
+
+ byte[] bytes = BinaryEncodingUtils.encode(enumDatas, EnumDatas.class);
+ EnumDatas decodeData = BinaryEncodingUtils.decode(bytes);
+ assertEquals(enumDatas.getLevel(), decodeData.getLevel());
+ }
+
+ /**
+ * 此测试用例是对序列化过程的验证,包括:头部、引用数据契约字段值、字段顺序的正确性;
+ */
+ @Test
+ public void testEncoding_Header_SingleRefContractField() {
+ DataContractRegistry.register(RefContractDatas.class);
+
+ RefContractDatasImpl refContractDatas = new RefContractDatasImpl();
+ PrimitiveDatasImpl primitiveDatas = new PrimitiveDatasImpl();
+ NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false);
+
+ primitiveDatas.setId(123);
+ primitiveDatas.setEnable(true);
+ primitiveDatas.setBoy((byte) 10);
+ primitiveDatas.setAge((short) 100);
+ primitiveDatas.setName("John");
+ primitiveDatas.setImage("Image of John".getBytes());
+ primitiveDatas.setFlag('x');
+ primitiveDatas.setValue(93239232);
+ primitiveDatas.setConfig(Bytes.fromString("Configuration of something."));
+ primitiveDatas.setNetworkAddress(networkAddress);
+
+ refContractDatas.setPrimitiveDatas(primitiveDatas);
+
+ byte[] bytes = BinaryEncodingUtils.encode(refContractDatas, RefContractDatas.class);
+ int offset = 0;
+ int code = BytesUtils.toInt(bytes, offset);
+ offset += 12;
+ assertEquals(0x08, code);
+
+ // 引用合约字段的字节;
+ byte[] primitiveDataFieldBytes = BytesEncoding.readInNormal(bytes, offset);
+ int maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]);
+
+ // 获得引用合约码与版本
+ int fieldOffset = 0;
+ int refCode = BytesUtils.toInt(primitiveDataFieldBytes, fieldOffset);
+ fieldOffset += 12;
+ assertEquals(0x05, refCode);
+
+ fieldOffset = assertFieldEncoding(refContractDatas.getPrimitive(), primitiveDataFieldBytes, fieldOffset);
+ assertEquals(primitiveDataFieldBytes.length, fieldOffset);
+ offset += primitiveDataFieldBytes.length + maskLen;
+ // 到了结尾;
+ assertEquals(bytes.length, offset);
+
+ }
+
+ /**
+ * 此测试用例是对序列化过程的验证,包括:头部、引用数据契约字段值、字段顺序的正确性;
+ */
+ @Test
+ public void testEncoding_Header_NullRefContractFields() {
+ DataContractRegistry.register(RefContractDatas.class);
+
+ RefContractDatasImpl refContractDatas = new RefContractDatasImpl();
+
+ byte[] bytes = BinaryEncodingUtils.encode(refContractDatas, RefContractDatas.class);
+ int offset = 0;
+ int code = BytesUtils.toInt(bytes, offset);
+ offset += 12;
+ assertEquals(0x08, code);
+
+ // 引用合约字段的字节;
+ byte[] primitiveDataFieldBytes = BytesEncoding.readInNormal(bytes, offset);
+ int maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]);
+ assertEquals(12, primitiveDataFieldBytes.length);
+
+ // 获得引用合约码与版本
+ int fieldOffset = 0;
+ int refCode = BytesUtils.toInt(primitiveDataFieldBytes, fieldOffset);
+ fieldOffset += 12;
+ assertEquals(0x05, refCode);
+
+ assertEquals(primitiveDataFieldBytes.length, fieldOffset);
+ offset += primitiveDataFieldBytes.length + maskLen;
+ // 到了结尾;
+ assertEquals(bytes.length, offset);
+ }
+
+ /**
+ * 此测试用例是对反序列化过程的验证,包括:头部、引用数据契约字段值、字段顺序的正确性;
+ */
+ @Test
+ public void testDecoding_Header_OrderedRefContractFields() {
+ DataContractRegistry.register(RefContractDatas.class);
+
+ RefContractDatasImpl refContractDatas = new RefContractDatasImpl();
+ PrimitiveDatasImpl primitiveDatas = new PrimitiveDatasImpl();
+ NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false);
+
+ primitiveDatas.setId(123);
+ primitiveDatas.setEnable(true);
+ primitiveDatas.setBoy((byte) 10);
+ primitiveDatas.setAge((short) 100);
+ primitiveDatas.setName("John");
+ primitiveDatas.setImage("Image of John".getBytes());
+ primitiveDatas.setFlag('x');
+ primitiveDatas.setValue(93239232);
+ primitiveDatas.setConfig(Bytes.fromString("Configuration of something."));
+ primitiveDatas.setNetworkAddress(networkAddress);
+
+ refContractDatas.setPrimitiveDatas(primitiveDatas);
+
+ byte[] bytes = BinaryEncodingUtils.encode(refContractDatas, RefContractDatas.class);
+ RefContractDatas decodeData = BinaryEncodingUtils.decode(bytes);
+
+ assertEquals(refContractDatas.getPrimitive().getId(), decodeData.getPrimitive().getId());
+ assertEquals(refContractDatas.getPrimitive().isEnable(), decodeData.getPrimitive().isEnable());
+ assertEquals(refContractDatas.getPrimitive().isBoy(), decodeData.getPrimitive().isBoy());
+ assertEquals(refContractDatas.getPrimitive().getAge(), decodeData.getPrimitive().getAge());
+ assertEquals(refContractDatas.getPrimitive().getName(), decodeData.getPrimitive().getName());
+ assertTrue(BytesUtils.equals(refContractDatas.getPrimitive().getImage(), decodeData.getPrimitive().getImage()));
+ assertEquals(refContractDatas.getPrimitive().getFlag(), decodeData.getPrimitive().getFlag());
+ assertEquals(refContractDatas.getPrimitive().getValue(), decodeData.getPrimitive().getValue());
+ assertEquals(refContractDatas.getPrimitive().getConfig(), decodeData.getPrimitive().getConfig());
+ assertEquals(refContractDatas.getPrimitive().getNetworkAddr().getHost(), decodeData.getPrimitive().getNetworkAddr().getHost());
+ assertEquals(refContractDatas.getPrimitive().getNetworkAddr().getPort(), decodeData.getPrimitive().getNetworkAddr().getPort());
+ }
+
+ /**
+ * 此测试用例是对序列化过程的验证,包括:头部、Generic引用数据契约字段值、字段顺序的正确性;
+ */
+ @Test
+ public void testEncoding_Header_GenericRefContractArrayFields() {
+
+ DataContractRegistry.register(GenericRefContractDatas.class);
+ DataContractRegistry.register(Operation.class);
+ DataContractRegistry.register(SubOperation.class);
+
+ GenericRefContractDatasImpl genericRefContractDatas = new GenericRefContractDatasImpl();
+ SubOperationImpl subOperation = new SubOperationImpl();
+ subOperation.setUserName("Jerry");
+
+ Operation[] operations = new Operation[1];
+ operations[0] = subOperation;
+ genericRefContractDatas.setOperations(operations);
+
+ byte[] bytes = BinaryEncodingUtils.encode(genericRefContractDatas, GenericRefContractDatas.class);
+ int offset = 0;
+ int code = BytesUtils.toInt(bytes, offset);
+ offset += 12;
+ assertEquals(0xb, code);
+
+ offset = assertGenericRefContractArrayFields(bytes, offset, operations);
+
+ // 到了结尾;
+ assertEquals(bytes.length, offset);
+ }
+
+ private int assertGenericRefContractArrayFields(byte[] bytes, int offset, Operation[] expectedOperations) {
+ // count of operations;
+ int opCount = NumberMask.NORMAL.resolveMaskedNumber(bytes, offset);
+ byte opCountHeadBytes = bytes[offset];
+ int maskLen = NumberMask.NORMAL.resolveMaskLength(opCountHeadBytes);
+ offset += maskLen;
+ assertEquals(expectedOperations.length, opCount);
+
+ // Field: operations;
+ for (int i = 0; i < opCount; i++) {
+ byte[] opertionItemBytes = BytesEncoding.readInNormal(bytes, offset);
+
+ int itemMaskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]);
+ offset += opertionItemBytes.length + itemMaskLen;
+
+ // verify subOperation;
+ int itemOffset = 0;
+ int itemCode = BytesUtils.toInt(opertionItemBytes, itemOffset);
+ itemOffset += 12;
+ assertEquals(0xa, itemCode);
+
+ byte[] userNameBytes = BytesEncoding.readInNormal(opertionItemBytes, itemOffset);
+ int nameMaskLen = NumberMask.NORMAL.resolveMaskLength(opertionItemBytes[itemOffset]);
+ itemOffset += userNameBytes.length + nameMaskLen;
+ String userName = BytesUtils.toString(userNameBytes);
+ assertEquals(((SubOperation) expectedOperations[i]).getUserName(), userName);
+
+ assertEquals(opertionItemBytes.length, itemOffset);
+ }
+
+ return offset;
+ }
+
+ /**
+ * 此测试用例是对反序列化过程的验证,包括:头部、Generic引用数据契约字段值、字段顺序的正确性;
+ */
+ @Test
+ public void testDecoding_Header_OrderedGenericRefContractFields() {
+ DataContractRegistry.register(GenericRefContractDatas.class);
+ DataContractRegistry.register(Operation.class);
+ DataContractRegistry.register(SubOperation.class);
+
+ GenericRefContractDatasImpl genericRefContractDatas = new GenericRefContractDatasImpl();
+ SubOperationImpl subOperation = new SubOperationImpl();
+ subOperation.setUserName("Jerry");
+
+ Operation[] operations = new Operation[1];
+ operations[0] = subOperation;
+ genericRefContractDatas.setOperations(operations);
+
+ byte[] bytes = BinaryEncodingUtils.encode(genericRefContractDatas, GenericRefContractDatas.class);
+
+ GenericRefContractDatas decodeData = BinaryEncodingUtils.decode(bytes);
+
+ assertEquals("Jerry", ((SubOperation) (decodeData.getOperations()[0])).getUserName());
+ }
+
+ /**
+ * 此测试用例是对序列化过程的验证,包括:头部、Primitive, Enum, 引用数据契约, Generic引用数据契约字段值、字段顺序的正确性;
+ */
+ @Test
+ public void testEncoding_Header_OrderedCompositeFields() {
+ DataContractRegistry.register(SubOperation.class);
+ DataContractRegistry.register(CompositeDatas.class);
+
+ CompositeDatasImpl compositeDatas = new CompositeDatasImpl();
+ PrimitiveDatasImpl primitiveDatas = new PrimitiveDatasImpl();
+ SubOperationImpl subOperation = new SubOperationImpl();
+ NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false);
+
+ compositeDatas.setEnable(false);
+ compositeDatas.setLevel(EnumLevel.V1);
+
+ primitiveDatas.setId(123);
+ primitiveDatas.setEnable(true);
+ primitiveDatas.setBoy((byte) 10);
+ primitiveDatas.setAge((short) 100);
+ primitiveDatas.setName("John");
+ primitiveDatas.setImage("Image of John".getBytes());
+ primitiveDatas.setFlag('x');
+ primitiveDatas.setValue(93239232);
+ primitiveDatas.setConfig(Bytes.fromString("Configuration of something."));
+ primitiveDatas.setNetworkAddress(networkAddress);
+
+ compositeDatas.setPrimitiveDatas(primitiveDatas);
+
+ subOperation.setUserName("Jerry");
+ Operation[] operations = new Operation[1];
+ operations[0] = subOperation;
+ compositeDatas.setOperations(operations);
+
+ byte[] bytes = BinaryEncodingUtils.encode(compositeDatas, CompositeDatas.class);
+
+ int offset = 0;
+ int code = BytesUtils.toInt(bytes, offset);
+ offset += 12;
+ assertEquals(0xc, code);
+
+ // primitive type
+ assertEquals(compositeDatas.isEnable() ? (byte) 1 : (byte) 0, bytes[offset]);
+ offset += 1;
+
+ byte enumCode = bytes[offset];
+ offset += 1;
+ assertEquals(compositeDatas.getLevel().CODE, enumCode);
+
+ // ----------------
+ // 引用合约字段的字节;
+ byte[] primitiveDataFieldBytes = BytesEncoding.readInNormal(bytes, offset);
+ int maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]);
+
+ // 获得引用合约码与版本
+ int fieldOffset = 0;
+ int primitiveDataCode = BytesUtils.toInt(primitiveDataFieldBytes, fieldOffset);
+ fieldOffset += 12;
+ assertEquals(0x05, primitiveDataCode);
+
+ fieldOffset = assertFieldEncoding(compositeDatas.getPrimitive(), primitiveDataFieldBytes, fieldOffset);
+ assertEquals(primitiveDataFieldBytes.length, fieldOffset);
+ offset += primitiveDataFieldBytes.length + maskLen;
+
+ // field: operation;
+ offset = assertGenericRefContractArrayFields(bytes, offset, operations);
+
+ // primitive
+ assertEquals(compositeDatas.getAge(), BytesUtils.toShort(bytes, offset));
+ offset += 2;
+
+ // 到了结尾;
+ assertEquals(bytes.length, offset);
+ }
+
+ /**
+ * 此测试用例是对反序列化过程的验证,包括:头部、Primitive, Enum, 引用数据契约, Generic引用数据契约字段值、字段顺序的正确性;
+ */
+ @Test
+ public void testDecoding_Header_OrderedCompositeFields() {
+ DataContractRegistry.register(SubOperation.class);
+ DataContractRegistry.register(CompositeDatas.class);
+
+ CompositeDatasImpl compositeDatas = new CompositeDatasImpl();
+ PrimitiveDatasImpl primitiveDatas = new PrimitiveDatasImpl();
+ SubOperationImpl subOperation = new SubOperationImpl();
+ NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false);
+
+ compositeDatas.setEnable(false);
+ compositeDatas.setLevel(EnumLevel.V1);
+ compositeDatas.setAge((short) 100);
+
+ primitiveDatas.setId(123);
+ primitiveDatas.setEnable(true);
+ primitiveDatas.setBoy((byte) 10);
+ primitiveDatas.setAge((short) 100);
+ primitiveDatas.setName("John");
+ primitiveDatas.setImage("Image of John".getBytes());
+ primitiveDatas.setFlag('x');
+ primitiveDatas.setValue(93239232);
+ primitiveDatas.setConfig(Bytes.fromString("Configuration of something."));
+ primitiveDatas.setNetworkAddress(networkAddress);
+
+ compositeDatas.setPrimitiveDatas(primitiveDatas);
+
+ subOperation.setUserName("Jerry");
+ Operation[] operations = new Operation[1];
+ operations[0] = subOperation;
+ compositeDatas.setOperations(operations);
+
+ byte[] bytes = BinaryEncodingUtils.encode(compositeDatas, CompositeDatas.class);
+ CompositeDatas decodeData = BinaryEncodingUtils.decode(bytes);
+
+ assertEquals(compositeDatas.isEnable(), decodeData.isEnable());
+ assertEquals(compositeDatas.getAge(), decodeData.getAge());
+ assertEquals(compositeDatas.getPrimitive().getId(), decodeData.getPrimitive().getId());
+ assertEquals(compositeDatas.getPrimitive().isEnable(), decodeData.getPrimitive().isEnable());
+ assertEquals(compositeDatas.getPrimitive().isBoy(), decodeData.getPrimitive().isBoy());
+ assertEquals(compositeDatas.getPrimitive().getAge(), decodeData.getPrimitive().getAge());
+ assertEquals(compositeDatas.getPrimitive().getName(), decodeData.getPrimitive().getName());
+ assertTrue(BytesUtils.equals(compositeDatas.getPrimitive().getImage(), decodeData.getPrimitive().getImage()));
+ assertEquals(compositeDatas.getPrimitive().getFlag(), decodeData.getPrimitive().getFlag());
+ assertEquals(compositeDatas.getPrimitive().getValue(), decodeData.getPrimitive().getValue());
+ assertEquals(compositeDatas.getPrimitive().getConfig(), decodeData.getPrimitive().getConfig());
+ assertEquals(compositeDatas.getPrimitive().getNetworkAddr().getHost(), decodeData.getPrimitive().getNetworkAddr().getHost());
+ assertEquals(compositeDatas.getPrimitive().getNetworkAddr().getPort(), decodeData.getPrimitive().getNetworkAddr().getPort());
+ assertEquals("Jerry", ((SubOperation) (decodeData.getOperations()[0])).getUserName());
+ assertEquals(compositeDatas.getLevel(), decodeData.getLevel());
+
+ }
+
+ /**
+ * 验证解析一个定义有顺序重复的两个字段的类型时,将引发异常 {@link DataContractException}
+ */
+ @Test
+ public void testFields_Order_confliction() {
+ DataContractException ex = null;
+ try {
+ DataContractRegistry.register(FieldOrderConflictedDatas.class);
+ } catch (DataContractException e) {
+ ex = e;
+ System.out.println(
+ "expected error of [" + FieldOrderConflictedDatas.class.toString() + "] --" + e.getMessage());
+ }
+ assertNotNull(ex);
+ }
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/CompositeDatas.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/CompositeDatas.java
new file mode 100644
index 00000000..be60a558
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/CompositeDatas.java
@@ -0,0 +1,29 @@
+package test.com.jd.blockchain.binaryproto;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * Created by zhangshuang3 on 2018/11/30.
+ */
+@DataContract(code = 0xc, name = "CompositeDatas", description = "")
+public interface CompositeDatas {
+
+ @DataField(order = 1, primitiveType = ValueType.BOOLEAN)
+ boolean isEnable();
+
+ @DataField(order = 2, refEnum = true)
+ EnumLevel getLevel();
+
+ @DataField(order = 3, refContract = true)
+ PrimitiveDatas getPrimitive();
+
+ @DataField(order=4, list = true, refContract=true, genericContract = true)
+ Operation[] getOperations();
+
+ @DataField(order = 5, primitiveType = ValueType.INT16)
+ short getAge();
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/CompositeDatasImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/CompositeDatasImpl.java
new file mode 100644
index 00000000..d1cbee50
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/CompositeDatasImpl.java
@@ -0,0 +1,68 @@
+package test.com.jd.blockchain.binaryproto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by zhangshuang3 on 2018/11/30.
+ */
+public class CompositeDatasImpl implements CompositeDatas{
+ private boolean enable;
+ private EnumLevel level;
+ PrimitiveDatas primitiveDatas;
+ private List operationList = new ArrayList();
+ private short age;
+
+ @Override
+ public boolean isEnable() {
+ return this.enable;
+ }
+
+ public void setEnable(boolean enable) {
+ this.enable = enable;
+ }
+
+ @Override
+ public EnumLevel getLevel() {
+ return this.level;
+ }
+
+ public void setLevel(EnumLevel level) {
+ this.level = level;
+ }
+
+ @Override
+ public PrimitiveDatas getPrimitive() {
+ return this.primitiveDatas;
+ }
+
+ public void setPrimitiveDatas(PrimitiveDatas primitiveDatas) {
+ this.primitiveDatas = primitiveDatas;
+ }
+
+ @Override
+ public Operation[] getOperations() {
+ return operationList.toArray(new Operation[operationList.size()]);
+ }
+
+ public void setOperations(Object[] operations) {
+ for (Object operation : operations) {
+ Operation op = (Operation)operation;
+ addOperation(op);
+ }
+ }
+
+ public void addOperation(Operation operation) {
+ operationList.add(operation);
+ }
+
+ @Override
+ public short getAge() {
+ return this.age;
+ }
+
+ public void setAge(short age) {
+ this.age = age;
+ }
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumDatas.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumDatas.java
new file mode 100644
index 00000000..629dc173
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumDatas.java
@@ -0,0 +1,15 @@
+package test.com.jd.blockchain.binaryproto;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+
+/**
+ * Created by zhangshuang3 on 2018/11/29.
+ */
+@DataContract(code = 0x07, name = "EnumDatas", description = "")
+public interface EnumDatas {
+
+ @DataField(order = 1, refEnum = true)
+ EnumLevel getLevel();
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumDatasImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumDatasImpl.java
new file mode 100644
index 00000000..f39e1b40
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumDatasImpl.java
@@ -0,0 +1,17 @@
+package test.com.jd.blockchain.binaryproto;
+
+/**
+ * Created by zhangshuang3 on 2018/11/29.
+ */
+public class EnumDatasImpl implements EnumDatas {
+ private EnumLevel level;
+
+ @Override
+ public EnumLevel getLevel() {
+ return this.level;
+ }
+ public void setLevel(EnumLevel level) {
+ this.level = level;
+ }
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumLevel.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumLevel.java
new file mode 100644
index 00000000..a736e334
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumLevel.java
@@ -0,0 +1,27 @@
+package test.com.jd.blockchain.binaryproto;
+
+import com.jd.blockchain.binaryproto.EnumContract;
+import com.jd.blockchain.binaryproto.EnumField;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * Created by zhangshuang3 on 2018/11/29.
+ */
+@EnumContract(code=0x0100, name = "EnumLevel", decription = "")
+public enum EnumLevel {
+
+ V1((byte) 1),
+
+ V2((byte) 2);
+
+ @EnumField(type= ValueType.INT8)
+ public final byte CODE;
+ public byte getCode() {
+ return CODE;
+ }
+ private EnumLevel(byte code) {
+ this.CODE = code;
+ }
+
+}
+
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/FieldOrderConflictedDatas.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/FieldOrderConflictedDatas.java
new file mode 100644
index 00000000..2c17922c
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/FieldOrderConflictedDatas.java
@@ -0,0 +1,33 @@
+package test.com.jd.blockchain.binaryproto;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * Created by zhangshuang3 on 2018/7/11.
+ */
+@DataContract(code = 0x06, name = "Primitive", description = "")
+public interface FieldOrderConflictedDatas {
+
+ @DataField(order = 2, primitiveType = ValueType.BOOLEAN)
+ boolean isEnable();
+
+ @DataField(order = 3, primitiveType = ValueType.INT8)
+ byte isBoy();
+
+ @DataField(order = 7, primitiveType = ValueType.INT16)
+ short getAge();
+
+ @DataField(order = -1, primitiveType = ValueType.INT32)
+ int getId();
+
+ @DataField(order = 6, primitiveType = ValueType.TEXT)
+ String getName();
+
+ @DataField(order = 7, primitiveType = ValueType.INT64)
+ long getValue();
+
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/FieldOrderConflictedDatasImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/FieldOrderConflictedDatasImpl.java
new file mode 100644
index 00000000..3c95639d
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/FieldOrderConflictedDatasImpl.java
@@ -0,0 +1,71 @@
+package test.com.jd.blockchain.binaryproto;
+
+/**
+ * Created by zhangshuang3 on 2018/7/11.
+ */
+public class FieldOrderConflictedDatasImpl implements FieldOrderConflictedDatas {
+
+ private boolean enable;
+
+ private byte boy;
+ private short age;
+ private int id;
+ private String name;
+
+ private long value;
+
+ @Override
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public short getAge() {
+ return age;
+ }
+
+ public void setAge(short age) {
+ this.age = age;
+ }
+
+ @Override
+ public byte isBoy() {
+ return this.boy;
+ }
+
+ public void setBoy(byte boy) {
+ this.boy = boy;
+ }
+
+ @Override
+ public boolean isEnable() {
+ return enable;
+ }
+
+ public void setEnable(boolean enable) {
+ this.enable = enable;
+ }
+
+ @Override
+ public long getValue() {
+ return value;
+ }
+
+ public void setValue(long value) {
+ this.value = value;
+ }
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/GenericRefContractDatas.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/GenericRefContractDatas.java
new file mode 100644
index 00000000..6c949d57
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/GenericRefContractDatas.java
@@ -0,0 +1,15 @@
+package test.com.jd.blockchain.binaryproto;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+
+/**
+ * Created by zhangshuang3 on 2018/11/29.
+ */
+@DataContract(code = 0xb, name = "GenericRefContractDatas", description = "")
+public interface GenericRefContractDatas {
+
+ @DataField(order=1, list = true, refContract=true, genericContract = true)
+ Operation[] getOperations();
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/GenericRefContractDatasImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/GenericRefContractDatasImpl.java
new file mode 100644
index 00000000..774dd5c7
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/GenericRefContractDatasImpl.java
@@ -0,0 +1,30 @@
+package test.com.jd.blockchain.binaryproto;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Created by zhangshuang3 on 2018/11/29.
+ */
+public class GenericRefContractDatasImpl implements GenericRefContractDatas{
+ private List operationList = new ArrayList();
+
+ @Override
+ public Operation[] getOperations() {
+ return operationList.toArray(new Operation[operationList.size()]);
+ }
+
+ public void setOperations(Object[] operations) {
+ for (Object operation : operations) {
+ Operation op = (Operation)operation;
+ addOperation(op);
+ }
+
+ }
+ public void addOperation(Operation operation) {
+ operationList.add(operation);
+ }
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/Operation.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/Operation.java
new file mode 100644
index 00000000..6f165db9
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/Operation.java
@@ -0,0 +1,10 @@
+package test.com.jd.blockchain.binaryproto;
+
+import com.jd.blockchain.binaryproto.DataContract;
+
+/**
+ * Created by zhangshuang3 on 2018/11/29.
+ */
+@DataContract(code = 0x09, name = "Operation", description = "")
+public interface Operation {
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/PrimitiveDatas.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/PrimitiveDatas.java
new file mode 100644
index 00000000..f855785c
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/PrimitiveDatas.java
@@ -0,0 +1,48 @@
+package test.com.jd.blockchain.binaryproto;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.ValueType;
+import com.jd.blockchain.utils.net.NetworkAddress;
+
+/**
+ * Created by zhangshuang3 on 2018/7/11.
+ */
+@DataContract(code = 0x05, name = "Primitive", description = "")
+public interface PrimitiveDatas {
+
+ @DataField(order = 2, primitiveType = ValueType.BOOLEAN)
+ boolean isEnable();
+
+ @DataField(order = 3, primitiveType = ValueType.INT8)
+ byte isBoy();
+
+ @DataField(order = 4, primitiveType = ValueType.INT16)
+ short getAge();
+
+ @DataField(order = -1, primitiveType = ValueType.INT32)
+ int getId();
+
+ @DataField(order = 6, primitiveType = ValueType.TEXT)
+ String getName();
+
+ @DataField(order = 7, primitiveType = ValueType.INT64)
+ long getValue();
+
+ @DataField(order = 12, primitiveType = ValueType.BYTES)
+ byte[] getImage();
+
+ @DataField(order = 100, primitiveType = ValueType.INT16)
+ char getFlag();
+
+ @DataField(order = 200, primitiveType = ValueType.BYTES)
+ Bytes getConfig();
+
+ @DataField(order = 201, primitiveType = ValueType.BYTES)
+ Bytes getSetting();
+
+ @DataField(order = 202, primitiveType = ValueType.BYTES)
+ NetworkAddress getNetworkAddr();
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/PrimitiveDatasImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/PrimitiveDatasImpl.java
new file mode 100644
index 00000000..ec0f6a4b
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/PrimitiveDatasImpl.java
@@ -0,0 +1,130 @@
+package test.com.jd.blockchain.binaryproto;
+
+import org.omg.CORBA.PUBLIC_MEMBER;
+
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.net.NetworkAddress;
+
+/**
+ * Created by zhangshuang3 on 2018/7/11.
+ */
+public class PrimitiveDatasImpl implements PrimitiveDatas {
+
+ private boolean enable;
+
+ private byte boy;
+ private short age;
+ private int id;
+ private String name;
+
+ private long value;
+
+ private char flag;
+
+ private byte[] image;
+
+ private Bytes config;
+
+ private Bytes setting;
+
+ private NetworkAddress networkAddress;
+
+ @Override
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public short getAge() {
+ return age;
+ }
+
+ public void setAge(short age) {
+ this.age = age;
+ }
+
+ @Override
+ public byte isBoy() {
+ return this.boy;
+ }
+
+ public void setBoy(byte boy) {
+ this.boy = boy;
+ }
+
+ @Override
+ public boolean isEnable() {
+ return enable;
+ }
+
+ public void setEnable(boolean enable) {
+ this.enable = enable;
+ }
+
+ @Override
+ public long getValue() {
+ return value;
+ }
+
+ @Override
+ public char getFlag() {
+ return flag;
+ }
+
+ public void setValue(long value) {
+ this.value = value;
+ }
+
+ public void setFlag(char flag) {
+ this.flag = flag;
+ }
+
+ @Override
+ public byte[] getImage() {
+ return image;
+ }
+
+ @Override
+ public Bytes getConfig() {
+ return config;
+ }
+
+ public void setImage(byte[] image) {
+ this.image = image;
+ }
+
+ public void setConfig(Bytes config) {
+ this.config = config;
+ }
+
+ public void setSetting(Bytes setting) {
+ this.setting = setting;
+ }
+
+ @Override
+ public Bytes getSetting() {
+ return setting;
+ }
+
+ @Override
+ public NetworkAddress getNetworkAddr() {
+ return networkAddress;
+ }
+
+ public void setNetworkAddress(NetworkAddress networkAddress) {
+ this.networkAddress = networkAddress;
+ }
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/RefContractDatas.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/RefContractDatas.java
new file mode 100644
index 00000000..4bc61955
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/RefContractDatas.java
@@ -0,0 +1,15 @@
+package test.com.jd.blockchain.binaryproto;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * Created by zhangshuang3 on 2018/11/29.
+ */
+@DataContract(code = 0x08, name = "RefContractDatas", description = "")
+public interface RefContractDatas {
+
+ @DataField(order = 1, refContract = true)
+ PrimitiveDatas getPrimitive();
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/RefContractDatasImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/RefContractDatasImpl.java
new file mode 100644
index 00000000..40880428
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/RefContractDatasImpl.java
@@ -0,0 +1,18 @@
+package test.com.jd.blockchain.binaryproto;
+
+/**
+ * Created by zhangshuang3 on 2018/11/29.
+ */
+public class RefContractDatasImpl implements RefContractDatas{
+ PrimitiveDatas primitiveDatas;
+
+ @Override
+ public PrimitiveDatas getPrimitive() {
+ return this.primitiveDatas;
+ }
+
+ public void setPrimitiveDatas(PrimitiveDatas primitiveDatas) {
+ this.primitiveDatas = primitiveDatas;
+ }
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/SubOperation.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/SubOperation.java
new file mode 100644
index 00000000..a72a6077
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/SubOperation.java
@@ -0,0 +1,16 @@
+package test.com.jd.blockchain.binaryproto;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * Created by zhangshuang3 on 2018/11/29.
+ */
+@DataContract(code = 0xa, name = "SubOperation", description = "")
+public interface SubOperation extends Operation {
+
+ @DataField(order=1, primitiveType = ValueType.TEXT)
+ String getUserName();
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/SubOperationImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/SubOperationImpl.java
new file mode 100644
index 00000000..6966fc63
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/SubOperationImpl.java
@@ -0,0 +1,17 @@
+package test.com.jd.blockchain.binaryproto;
+
+/**
+ * Created by zhangshuang3 on 2018/11/29.
+ */
+public class SubOperationImpl implements SubOperation {
+ String userName;
+
+ @Override
+ public String getUserName() {
+ return this.userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Address.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Address.java
new file mode 100644
index 00000000..013d414e
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Address.java
@@ -0,0 +1,16 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+//
+//import com.jd.blockchain.binaryproto.DataContract;
+//import com.jd.blockchain.binaryproto.DataField;
+//import com.jd.blockchain.binaryproto.ValueType;
+//
+//@DataContract(code=0x02, name="Address" , description="")
+//public interface Address {
+//
+// @DataField(order=1, primitiveType=ValueType.TEXT)
+// String getStreet();
+//
+// @DataField(order=2, primitiveType=ValueType.INT32)
+// int getNumber();
+//
+//}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/AddressCodeDuplicate.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/AddressCodeDuplicate.java
new file mode 100644
index 00000000..7e60715e
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/AddressCodeDuplicate.java
@@ -0,0 +1,19 @@
+package test.com.jd.blockchain.binaryproto.contract;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * Created by zhangshuang3 on 2018/7/9.
+ */
+@DataContract(code=0x02, name="Address" , description="")
+public interface AddressCodeDuplicate {
+
+ @DataField(order=1, primitiveType= ValueType.TEXT)
+ String getStreet();
+
+ @DataField(order=2, primitiveType=ValueType.INT32)
+ int getNumber();
+
+}
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/AddressOrderDuplicate.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/AddressOrderDuplicate.java
new file mode 100644
index 00000000..cf23898f
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/AddressOrderDuplicate.java
@@ -0,0 +1,19 @@
+package test.com.jd.blockchain.binaryproto.contract;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * Created by zhangshuang3 on 2018/7/9.
+ */
+@DataContract(code=0x03, name="Address" , description="")
+public interface AddressOrderDuplicate {
+
+ @DataField(order=1, primitiveType= ValueType.TEXT)
+ String getStreet();
+
+ @DataField(order=1, primitiveType=ValueType.INT32)
+ int getNumber();
+
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Array.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Array.java
new file mode 100644
index 00000000..802cfb15
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Array.java
@@ -0,0 +1,25 @@
+package test.com.jd.blockchain.binaryproto.contract;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * Created by zhangshuang3 on 2018/7/11.
+ */
+@DataContract(code=0x08, name="Array" , description="")
+public interface Array {
+
+ @DataField(order=1, primitiveType= ValueType.INT32, list=true)
+ int[] getScores();
+
+ @DataField(order=2, primitiveType=ValueType.TEXT, list=true)
+ String[] getFeatures();
+
+ @DataField(order=3, primitiveType=ValueType.BYTES)
+ byte[] getFamilyMemberAges();
+
+ @DataField(order=4, primitiveType=ValueType.INT64, list=true)
+ long[] getFamilyMemberIds();
+
+}
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/CryptoAlgorithm.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/CryptoAlgorithm.java
new file mode 100644
index 00000000..d4900959
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/CryptoAlgorithm.java
@@ -0,0 +1,158 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+//
+//import com.jd.blockchain.binaryproto.EnumContract;
+//import com.jd.blockchain.binaryproto.EnumField;
+//import com.jd.blockchain.binaryproto.ValueType;
+//import com.jd.blockchain.crypto.CryptoAlgorithmType;
+//import my.utils.io.BytesUtils;
+//
+///**
+// * Created by zhangshuang3 on 2018/7/30.
+// */
+//@EnumContract(code=0x0102)
+//public enum CryptoAlgorithm {
+//
+// // Hash 类;
+// // SHA_128(CryptoAlgorithmMask.HASH, (byte) 0x01, false, false),
+//
+// SHA_256(CryptoAlgorithmType.HASH, (byte) 0x01, false, false),
+//
+// RIPLE160(CryptoAlgorithmType.HASH, (byte) 0x02, false, false),
+//
+// SM3(CryptoAlgorithmType.HASH, (byte) 0x03, false, false),
+//
+// // 非对称签名/加密算法;
+//
+// /**
+// * RSA 签名算法;可签名,可加密;
+// */
+// RSA(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x01, true, true),
+//
+// /**
+// * ED25519 签名算法;只用于签名,没有加密特性;
+// */
+// ED25519(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x02, true, false),
+//
+// /**
+// * ECDSA 签名算法;只用于签名,没有加密特性;???
+// */
+// ECDSA(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x03, true, false),
+//
+// /**
+// * 国密 SM2 算法;可签名,可加密;
+// */
+// SM2(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x04, true, true),
+//
+// // 对称加密;
+// /**
+// * AES 算法;可加密;
+// */
+// AES(CryptoAlgorithmType.SYMMETRIC, (byte) 0x01, false, true),
+//
+// SM4(CryptoAlgorithmType.SYMMETRIC, (byte) 0x02, false, true),
+//
+// // 随机性;
+// /**
+// * ????? 一种随机数算法,待定;
+// */
+// JAVA_SECURE(CryptoAlgorithmType.RANDOM, (byte) 0x01, false, false);
+//
+// /**
+// * 密码算法的代号;
+// * 注:只占16位;
+// */
+// @EnumField(type = ValueType.INT8)
+// public final byte CODE;
+//
+// private final boolean signable;
+//
+// private final boolean encryptable;
+//
+// private CryptoAlgorithm(byte algType, byte algId, boolean signable, boolean encryptable) {
+// this.CODE = (byte) (algType | algId);
+// this.signable = signable;
+// this.encryptable = encryptable;
+// }
+//
+// /**
+// * 是否属于摘要算法;
+// *
+// * @return
+// */
+// public boolean isHash() {
+// return (CODE & CryptoAlgorithmType.HASH) == CryptoAlgorithmType.HASH;
+// }
+//
+// /**
+// * 是否属于非对称密码算法;
+// *
+// * @return
+// */
+// public boolean isAsymmetric() {
+// return (CODE & CryptoAlgorithmType.ASYMMETRIC) == CryptoAlgorithmType.ASYMMETRIC;
+// }
+//
+// /**
+// * 是否属于对称密码算法;
+// *
+// * @return
+// */
+// public boolean isSymmetric() {
+// return (CODE & CryptoAlgorithmType.SYMMETRIC) == CryptoAlgorithmType.SYMMETRIC;
+// }
+//
+// /**
+// * 是否属于随机数算法;
+// *
+// * @return
+// */
+// public boolean isRandom() {
+// return (CODE & CryptoAlgorithmType.RANDOM) == CryptoAlgorithmType.RANDOM;
+// }
+//
+// /**
+// * 是否支持签名操作;
+// *
+// * @return
+// */
+// public boolean isSignable() {
+// return signable;
+// }
+//
+// /**
+// * 是否支持加密操作;
+// *
+// * @return
+// */
+// public boolean isEncryptable() {
+// return encryptable;
+// }
+//
+// /**
+// * 返回指定编码对应的枚举实例;
+// *
+// * 如果不存在,则返回 null;
+// *
+// * @param code
+// * @return
+// */
+// public static CryptoAlgorithm valueOf(byte code) {
+// for (CryptoAlgorithm alg : CryptoAlgorithm.values()) {
+// if (alg.CODE == code) {
+// return alg;
+// }
+// }
+// throw new IllegalArgumentException("CryptoAlgorithm doesn't support enum code[" + code + "]!");
+// }
+//
+// // /**
+// // * @return
+// // */
+// // public byte[] toBytes() {
+// // byte[] bytes = BytesUtils.toBytes(CODE);
+// // byte[] result = new byte[BYTES_SIZE];
+// // System.arraycopy(bytes, 2, result, 0, 2);
+// // // TODO: 只返回最后2个字节;
+// // return result;
+// // }
+//}
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/CryptoSetting.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/CryptoSetting.java
new file mode 100644
index 00000000..35662d50
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/CryptoSetting.java
@@ -0,0 +1,41 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+//
+//import com.jd.blockchain.binaryproto.DataContract;
+//import com.jd.blockchain.binaryproto.DataField;
+//import com.jd.blockchain.binaryproto.ValueType;
+//
+///**
+// * Created by zhangshuang3 on 2018/7/30.
+// */
+//@DataContract(code=0x0d, name="CryptoSetting", description = "Crypto setting")
+//public interface CryptoSetting {
+//
+// /**
+// * 系统中使用的 Hash 算法;
+// *
+// * 对于历史数据,如果它未发生更改,则总是按照该数据产生时采用的算法进行校验,即使当时指定的Hash算法和当前的不同;
+// *
+// * 如果对数据进行了更新,则采用新的 Hash 算法来计算生成完整性证明;
+// *
+// * @return
+// */
+// @DataField(order=1, refEnum=true)
+// public HashAlgorithm getHashAlgorithm();
+//
+// @DataField(order=2, refEnum=true)
+// public CryptoAlgorithm getHashAlgorithm1();
+//
+// /**
+// * 当有完整性证明的数据被从持久化介质中加载时,是否对其进行完整性校验(重新计算 hash 比对是否一致);
+// *
+// * 如果为 true ,则自动进行校验,如果校验失败,会引发异常;
+// *
+// * 注意:开启此选项将对性能会产生负面影响,因此使用者需要在性能和数据安全性之间做出权衡;
+// *
+// * @return
+// */
+// @DataField(order=3, primitiveType= ValueType.BOOLEAN)
+// public boolean getAutoVerifyHash();//func name is getxxxxx type
+//
+//}
+//
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/HashAlgorithm.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/HashAlgorithm.java
new file mode 100644
index 00000000..7f8296ff
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/HashAlgorithm.java
@@ -0,0 +1,50 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+//
+//import com.jd.blockchain.binaryproto.EnumContract;
+//import com.jd.blockchain.binaryproto.EnumField;
+//import com.jd.blockchain.binaryproto.ValueType;
+//
+///**
+// * Created by zhangshuang3 on 2018/7/30.
+// */
+//@EnumContract(code=0x0101)
+//public enum HashAlgorithm {
+//
+// RIPE160((byte) 1),
+//
+// SHA256((byte) 2),
+//
+// SM3((byte) 4);
+//
+// @EnumField(type = ValueType.INT8)
+// public final byte CODE;
+//
+// private HashAlgorithm(byte algorithm) {
+// CODE = algorithm;
+// }
+//
+// public byte getAlgorithm() {
+// return CODE;
+// }
+//
+// public static HashAlgorithm valueOf(byte algorithm) {
+// for (HashAlgorithm hashAlgorithm : HashAlgorithm.values()) {
+// if (hashAlgorithm.CODE == algorithm) {
+// return hashAlgorithm;
+// }
+// }
+// throw new IllegalArgumentException("Unsupported hash algorithm [" + algorithm + "]!");
+// }
+//
+// public static void checkHashAlgorithm(HashAlgorithm algorithm) {
+// switch (algorithm) {
+// case RIPE160:
+// break;
+// case SHA256:
+// break;
+// default:
+// throw new IllegalArgumentException("Unsupported hash algorithm [" + algorithm + "]!");
+// }
+// }
+//}
+//
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerBlock.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerBlock.java
new file mode 100644
index 00000000..74e206be
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerBlock.java
@@ -0,0 +1,28 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+//
+//import com.jd.blockchain.binaryproto.DataContract;
+//import com.jd.blockchain.binaryproto.DataField;
+//import com.jd.blockchain.binaryproto.ValueType;
+//import com.jd.blockchain.crypto.hash.HashDigest;
+//
+///**
+// * Created by zhangshuang3 on 2018/7/30.
+// */
+//@DataContract(code=0x12, name="LedgerBlock", description ="LedgerBlock")
+//public interface LedgerBlock extends LedgerDataSnapshot{
+//
+// @DataField(order=1, refHashDigest=true)
+// HashDigest getHash();
+//
+// @DataField(order=2, refHashDigest=true)
+// HashDigest getPreviousHash();
+//
+// @DataField(order=3, refHashDigest=true)
+// HashDigest getLedgerHash();
+//
+// @DataField(order=4, primitiveType=ValueType.INT64)
+// long getHeight();
+//
+// @DataField(order=5, refHashDigest=true)
+// HashDigest getTransactionSetHash();
+//}
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerDataSnapshot.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerDataSnapshot.java
new file mode 100644
index 00000000..683e13f4
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerDataSnapshot.java
@@ -0,0 +1,34 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+//
+//import com.jd.blockchain.binaryproto.DataContract;
+//import com.jd.blockchain.binaryproto.DataField;
+//import com.jd.blockchain.crypto.hash.HashDigest;
+//
+///**
+// * Created by zhangshuang3 on 2018/7/30.
+// */
+//@DataContract(code=0x11, name="LedgerDataSnapshot", description ="LedgerDataSnapshot")
+//public interface LedgerDataSnapshot {
+//
+// @DataField(order=1, refHashDigest=true)
+// HashDigest getAdminAccountHash();
+//
+// @DataField(order=2, refHashDigest=true)
+// HashDigest getUserAccountSetHash();
+//
+// @DataField(order=3, refHashDigest=true)
+// HashDigest getUserPrivilegeHash();
+//
+// @DataField(order=4, refHashDigest=true)
+// HashDigest getDataAccountSetHash();
+//
+// @DataField(order=5, refHashDigest=true)
+// HashDigest getDataPrivilegeHash();
+//
+// @DataField(order=6, refHashDigest=true)
+// HashDigest getContractAccountSetHash();
+//
+// @DataField(order=7, refHashDigest=true)
+// HashDigest getContractPrivilegeHash();
+//
+//}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerMetadata.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerMetadata.java
new file mode 100644
index 00000000..d40babdf
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerMetadata.java
@@ -0,0 +1,25 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+//
+//import com.jd.blockchain.binaryproto.DataContract;
+//import com.jd.blockchain.binaryproto.DataField;
+//import com.jd.blockchain.binaryproto.ValueType;
+//
+///**
+// * Created by zhangshuang3 on 2018/7/30.
+// */
+//@DataContract(code=0x0b, name="LedgerMetadata", description = "Ledger meta data")
+//public interface LedgerMetadata {
+//
+// @DataField(order=1, primitiveType= ValueType.INT8, list=true)
+// byte[] getSeed();
+//
+// @DataField(order = 2, refContract=true)
+// LedgerSetting getSetting();
+//
+// @DataField(order=3, primitiveType=ValueType.INT8, list=true)
+// byte[] getPrivilegesHash();
+//
+// @DataField(order=4, primitiveType=ValueType.INT8, list=true)
+// byte[] getParticipantsHash();
+//
+//}
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerSetting.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerSetting.java
new file mode 100644
index 00000000..6f2f383b
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerSetting.java
@@ -0,0 +1,21 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+//
+//import com.jd.blockchain.binaryproto.DataContract;
+//import com.jd.blockchain.binaryproto.DataField;
+//
+///**
+// * Created by zhangshuang3 on 2018/7/30.
+// */
+//@DataContract(code=0x0c, name="LedgerSetting", description = "Ledger setting")
+//public interface LedgerSetting {
+//
+// //@DataField(order=1, refContract=true)
+// //ConsensusSetting getConsensusSetting();
+//
+// @DataField(order=2, refContract=true)
+// CryptoSetting getCryptoSetting();
+//
+// @DataField(order=3, refContract=true)
+// PrivilegeModelSetting getPrivilegesModelSetting();
+//
+//}
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Level.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Level.java
new file mode 100644
index 00000000..7b74768c
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Level.java
@@ -0,0 +1,25 @@
+package test.com.jd.blockchain.binaryproto.contract;
+
+import com.jd.blockchain.binaryproto.EnumContract;
+import com.jd.blockchain.binaryproto.EnumField;
+import com.jd.blockchain.utils.ValueType;
+
+@EnumContract(code=0x0100)
+public enum Level {
+
+ V1((byte) 1),
+
+ V2((byte) 2);
+
+ @EnumField(type=ValueType.INT8)
+ public final byte CODE;
+ public byte getCode() {
+ return CODE;
+ }
+ private Level(byte code) {
+ this.CODE = code;
+ }
+
+}
+
+
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Privilege.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Privilege.java
new file mode 100644
index 00000000..3b1d53de
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Privilege.java
@@ -0,0 +1,18 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+//
+//import com.jd.blockchain.binaryproto.DataContract;
+//import com.jd.blockchain.binaryproto.DataField;
+//import com.jd.blockchain.binaryproto.ValueType;
+//
+///**
+// * Created by zhangshuang3 on 2018/7/30.
+// */
+//@DataContract(code=0x10, name="Privilege", description ="Privilege")
+//public interface Privilege {
+//
+// //SortedSet getOpCodes(); implement later
+//
+// @DataField(order=2, primitiveType= ValueType.INT64)
+// long getVersion();
+//
+//}
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/PrivilegeModelSetting.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/PrivilegeModelSetting.java
new file mode 100644
index 00000000..65df415b
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/PrivilegeModelSetting.java
@@ -0,0 +1,20 @@
+package test.com.jd.blockchain.binaryproto.contract;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * Created by zhangshuang3 on 2018/7/30.
+ */
+@DataContract(code=0x0f, name="PrivilegeModelSetting", description ="Privilege Model setting")
+public interface PrivilegeModelSetting {
+
+ @DataField(order=1, primitiveType= ValueType.INT64)
+ long getLatestVersion();
+
+ //@DataField(order=2, refContract=true)
+ //Privilege getPrivilege(long version);
+
+}
+
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/RefContract.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/RefContract.java
new file mode 100644
index 00000000..0decffb1
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/RefContract.java
@@ -0,0 +1,20 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+
+//import com.jd.blockchain.binaryproto.DataContract;
+//import com.jd.blockchain.binaryproto.DataField;
+
+/**
+ * Created by zhangshuang3 on 2018/7/11.
+ */
+/*
+@DataContract(code=0x07, name="RefContract" , description="")
+public interface RefContract {
+
+ @DataField(order=1, refContract=true)
+ Address getAddress();
+
+ @DataField(order=2, refContract=true)
+ Address getAddress1();
+
+}
+*/
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/RefEnum.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/RefEnum.java
new file mode 100644
index 00000000..793d0457
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/RefEnum.java
@@ -0,0 +1,15 @@
+package test.com.jd.blockchain.binaryproto.contract;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+
+/**
+ * Created by zhangshuang3 on 2018/7/11.
+ */
+@DataContract(code=0x06, name="RefEnum" , description="")
+public interface RefEnum {
+
+ @DataField(order=1, refEnum=true)
+ Level getLevel();
+
+}
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Student.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Student.java
new file mode 100644
index 00000000..29d6dae5
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Student.java
@@ -0,0 +1,41 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+
+//import com.jd.blockchain.binaryproto.DataContract;
+//import com.jd.blockchain.binaryproto.DataField;
+//import com.jd.blockchain.binaryproto.ValueType;
+
+/*
+@DataContract(code=0x01, name="测试数据契约1", description = "用于测试的数据契约")
+public interface Student {
+
+ @DataField(order=1, primitiveType=ValueType.INT32)
+ int getId();
+
+ @DataField(order=2, primitiveType=ValueType.INT8)
+ byte getFamilyMemberNum();
+
+ @DataField(order=3, primitiveType=ValueType.TEXT)
+ String getName();
+
+ @DataField(order=4, primitiveType=ValueType.INT16)
+ short getAge();
+
+ @DataField(order=5, refEnum=true)
+ Level getLevel();
+
+ @DataField(order=6, refContract=true)
+ Address getAddress();
+
+ @DataField(order=7, primitiveType=ValueType.INT32, list=true)
+ int[] getScores();
+
+ @DataField(order=8, primitiveType=ValueType.TEXT, list=true)
+ String[] getFeatures();
+
+ @DataField(order=9, primitiveType=ValueType.INT8, list=true)
+ byte[] getFamilyMemberAges();
+
+ @DataField(order=10, primitiveType=ValueType.INT64, list=true)
+ long[] getFamilyMemberIds();
+}
+*/
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/StudentInvert.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/StudentInvert.java
new file mode 100644
index 00000000..b82f23c1
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/StudentInvert.java
@@ -0,0 +1,44 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+
+//import com.jd.blockchain.binaryproto.DataContract;
+//import com.jd.blockchain.binaryproto.DataField;
+//import com.jd.blockchain.binaryproto.ValueType;
+
+/**
+ * Created by zhangshuang3 on 2018/7/9.
+ */
+/*
+@DataContract(code=0x04, name="测试数据契约1", description = "用于测试的数据契约")
+public interface StudentInvert {
+
+ @DataField(order=10, primitiveType=ValueType.INT64, list=true)
+ long[] getFamilyMemberIds();
+
+ @DataField(order=9, primitiveType=ValueType.INT8, list=true)
+ byte[] getFamilyMemberAges();
+
+ @DataField(order=8, primitiveType=ValueType.TEXT, list=true)
+ String[] getFeatures();
+
+ @DataField(order=7, primitiveType=ValueType.INT32, list=true)
+ int[] getScores();
+
+ @DataField(order=6, refContract=true)
+ Address getAddress();
+
+ @DataField(order=5, refEnum=true)
+ Level getLevel();
+
+ @DataField(order=4, primitiveType=ValueType.INT16)
+ short getAge();
+
+ @DataField(order=3, primitiveType=ValueType.TEXT)
+ String getName();
+
+ @DataField(order=2, primitiveType=ValueType.INT8)
+ byte getFamilyMemberNum();
+
+ @DataField(order=1, primitiveType= ValueType.INT32)
+ int getId();
+}
+*/
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/User.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/User.java
new file mode 100644
index 00000000..fa6adef1
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/User.java
@@ -0,0 +1,31 @@
+//package test.com.jd.blockchain.binaryproto.contract;
+
+//import com.jd.blockchain.binaryproto.DataContract;
+//import com.jd.blockchain.binaryproto.DataField;
+//import com.jd.blockchain.binaryproto.ValueType;
+//import com.jd.blockchain.crypto.asymmetric.PubKey;
+//import my.utils.io.ByteArray;
+
+/**
+ * Created by zhangshuang3 on 2018/7/25.
+ */
+/*
+@DataContract(code=0x09, name="UserAccount", description = "用户账户")
+public interface User {
+
+ @DataField(order=1, primitiveType= ValueType.TEXT)
+ String getAddress();
+
+ @DataField(order=2, refPubKey = true)
+ PubKey getPubKey();
+
+ @DataField(order=3, refPubKey = true)
+ PubKey getDataPubKey();
+
+ @DataField(order=4, primitiveType = ValueType.BYTES)
+ ByteArray getDataHash();
+
+ @DataField(order=5, primitiveType = ValueType.BYTES)
+ ByteArray getPrivilegeHash();
+}
+*/
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/AddressImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/AddressImpl.java
new file mode 100644
index 00000000..c2af3153
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/AddressImpl.java
@@ -0,0 +1,29 @@
+/*
+package test.com.jd.blockchain.binaryproto.contract.impl;
+
+import test.com.jd.blockchain.binaryproto.contract.Address;
+
+public class AddressImpl implements Address {
+ private String street;
+ private int number;
+
+ @Override
+ public String getStreet() {
+ // TODO Auto-generated method stub
+ return this.street;
+ }
+ public void setStreet(String street) {
+ this.street = street;
+ }
+
+ @Override
+ public int getNumber() {
+ // TODO Auto-generated method stub
+ return this.number;
+ }
+ public void setNumber(int number) {
+ this.number = number;
+ }
+
+}
+*/
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/ArrayImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/ArrayImpl.java
new file mode 100644
index 00000000..6c9cd628
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/ArrayImpl.java
@@ -0,0 +1,47 @@
+package test.com.jd.blockchain.binaryproto.contract.impl;
+
+import test.com.jd.blockchain.binaryproto.contract.*;
+
+/**
+ * Created by zhangshuang3 on 2018/7/11.
+ */
+public class ArrayImpl implements Array {
+
+ private byte[] familyMemberAges;
+ private int[] scores;
+ private String[] features;
+ private long[] familyMemberIds;
+
+ @Override
+ public int[] getScores() {
+ // TODO Auto-generated method stub
+ return this.scores;
+ }
+ public void setScores(int[] scores) {
+ this.scores = scores;
+ }
+
+ @Override
+ public String[] getFeatures() {
+ return this.features;
+ }
+ public void setFeatures(String[] features) {
+ this.features = features;
+ }
+
+ @Override
+ public byte[] getFamilyMemberAges() {
+ return this.familyMemberAges;
+ }
+ public void setFamilyMemberAges(byte[] familyMemberAge) {
+ this.familyMemberAges = familyMemberAge;
+ }
+
+ @Override
+ public long[] getFamilyMemberIds() {
+ return this.familyMemberIds;
+ }
+ public void setFamilyMemberIds(long[] familyMemberId) {
+ this.familyMemberIds = familyMemberId;
+ }
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/CryptoSettingImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/CryptoSettingImpl.java
new file mode 100644
index 00000000..2fe4cb23
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/CryptoSettingImpl.java
@@ -0,0 +1,40 @@
+//package test.com.jd.blockchain.binaryproto.contract.impl;
+
+//import test.com.jd.blockchain.binaryproto.contract.CryptoAlgorithm;
+//import test.com.jd.blockchain.binaryproto.contract.CryptoSetting;
+//import test.com.jd.blockchain.binaryproto.contract.HashAlgorithm;
+
+/**
+ * Created by zhangshuang3 on 2018/7/30.
+ */
+/*
+public class CryptoSettingImpl implements CryptoSetting {
+ private HashAlgorithm hashAlgorithm;
+ private CryptoAlgorithm cryptoAlgorithm;
+ private boolean isAuto;
+
+ @Override
+ public HashAlgorithm getHashAlgorithm() {
+ return this.hashAlgorithm;
+ }
+ public void setHashAlgorithm(HashAlgorithm hashAlgorithm) {
+ this.hashAlgorithm = hashAlgorithm;
+ }
+
+ @Override
+ public CryptoAlgorithm getHashAlgorithm1() {
+ return this.cryptoAlgorithm;
+ }
+ public void setHashAlgorithm1(CryptoAlgorithm cryptoAlgorithm) {
+ this.cryptoAlgorithm = cryptoAlgorithm;
+ }
+ @Override
+ public boolean getAutoVerifyHash() {
+ return isAuto;
+ }
+ public void setAutoVerifyHash(boolean isAuto) {
+ this.isAuto = isAuto;
+ }
+
+}
+*/
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerBlockImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerBlockImpl.java
new file mode 100644
index 00000000..803b4b5a
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerBlockImpl.java
@@ -0,0 +1,147 @@
+//package test.com.jd.blockchain.binaryproto.contract.impl;
+
+//import com.jd.blockchain.binaryproto.DConstructor;
+//import com.jd.blockchain.binaryproto.DataField;
+//import com.jd.blockchain.binaryproto.FieldSetter;
+//import com.jd.blockchain.crypto.hash.HashDigest;
+//import test.com.jd.blockchain.binaryproto.contract.Address;
+//import test.com.jd.blockchain.binaryproto.contract.LedgerBlock;
+
+/**
+ * Created by zhangshuang3 on 2018/7/30.
+ */
+/*
+public class LedgerBlockImpl implements LedgerBlock {
+
+ private HashDigest hash;
+
+ private long height;
+
+ private HashDigest ledgerHash;
+
+ private HashDigest previousHash;
+
+ private HashDigest adminAccountHash;
+
+ private HashDigest userAccountSetHash;
+
+ private HashDigest userPrivilegeHash;
+
+ private HashDigest dataAccountSetHash;
+
+ private HashDigest dataPrivilegeHash;
+
+ private HashDigest contractAccountSetHash;
+
+ private HashDigest contractPrivilegeHash;
+
+ private HashDigest transactionSetHash;
+
+ public void setAdminAccountHash(HashDigest adminAccountHash) {
+ this.adminAccountHash = adminAccountHash;
+ }
+
+ public void setUserAccountSetHash(HashDigest userAccountSetHash) {
+ this.userAccountSetHash = userAccountSetHash;
+ }
+
+ public void setUserPrivilegeHash(HashDigest userPrivilegeHash) {
+ this.userPrivilegeHash = userPrivilegeHash;
+ }
+
+ public void setDataAccountSetHash(HashDigest dataAccountSetHash) {
+ this.dataAccountSetHash = dataAccountSetHash;
+ }
+
+ public void setDataPrivilegeHash(HashDigest dataPrivilegeHash) {
+ this.dataPrivilegeHash = dataPrivilegeHash;
+ }
+
+ public void setContractAccountSetHash(HashDigest contractAccountSetHash) {
+ this.contractAccountSetHash = contractAccountSetHash;
+ }
+
+ public void setContractPrivilegeHash(HashDigest contractPrivilegeHash) {
+ this.contractPrivilegeHash = contractPrivilegeHash;
+ }
+
+ public void setTransactionSetHash(HashDigest transactionSetHash) {
+ this.transactionSetHash = transactionSetHash;
+ }
+
+ public LedgerBlockImpl() {
+ }
+
+ @DConstructor(name="LedgerBlockImpl")
+ public LedgerBlockImpl(@FieldSetter(name="getHeight", type="long") long height, @FieldSetter(name="getLedgerHash", type="HashDigest") HashDigest ledgerHash, @FieldSetter(name="getPreviousHash", type="HashDigest") HashDigest previousHash) {
+ this.height = height;
+ this.ledgerHash = ledgerHash;
+ this.previousHash = previousHash;
+ }
+
+ @Override
+ public HashDigest getHash() {
+ return hash;
+ }
+
+ @Override
+ public HashDigest getPreviousHash() {
+ return previousHash;
+ }
+
+ @Override
+ public HashDigest getLedgerHash() {
+ return ledgerHash;
+ }
+
+ @Override
+ public long getHeight() {
+ return height;
+ }
+
+ @Override
+ public HashDigest getAdminAccountHash() {
+ return adminAccountHash;
+ }
+
+ @Override
+ public HashDigest getUserAccountSetHash() {
+ return userAccountSetHash;
+ }
+
+ @Override
+ public HashDigest getUserPrivilegeHash() {
+ return userPrivilegeHash;
+ }
+
+ @Override
+ public HashDigest getDataAccountSetHash() {
+ return dataAccountSetHash;
+ }
+
+ @Override
+ public HashDigest getDataPrivilegeHash() {
+ return dataPrivilegeHash;
+ }
+
+ @Override
+ public HashDigest getContractAccountSetHash() {
+ return contractAccountSetHash;
+ }
+
+ @Override
+ public HashDigest getContractPrivilegeHash() {
+ return contractPrivilegeHash;
+ }
+
+ @Override
+ public HashDigest getTransactionSetHash() {
+ return transactionSetHash;
+ }
+
+ public void setHash(HashDigest blockHash) {
+ this.hash = blockHash;
+ }
+
+}
+*/
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerMetadataImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerMetadataImpl.java
new file mode 100644
index 00000000..7267377b
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerMetadataImpl.java
@@ -0,0 +1,56 @@
+//package test.com.jd.blockchain.binaryproto.contract.impl;
+
+//import test.com.jd.blockchain.binaryproto.contract.*;
+//import test.com.jd.blockchain.binaryproto.contract.LedgerMetadata;
+//import test.com.jd.blockchain.binaryproto.contract.LedgerSetting;
+
+/**
+ * Created by zhangshuang3 on 2018/7/30.
+ */
+/*
+public class LedgerMetadataImpl implements LedgerMetadata {
+ private byte[] seed;
+
+ private LedgerSetting setting;
+
+ private byte[] privilegesHash;
+
+ private byte[] participantsHash;
+
+ @Override
+ public byte[] getSeed() {
+ return seed;
+ }
+
+ @Override
+ public LedgerSetting getSetting() {
+ return setting;
+ }
+
+ @Override
+ public byte[] getPrivilegesHash() {
+ return privilegesHash;
+ }
+
+ @Override
+ public byte[] getParticipantsHash() {
+ return participantsHash;
+ }
+
+ public void setSeed(byte[] seed) {
+ this.seed = seed;
+ }
+
+ public void setSetting(LedgerSetting setting) {
+ this.setting = setting;
+ }
+
+ public void setPrivilegesHash(byte[] privilegesHash) {
+ this.privilegesHash = privilegesHash;
+ }
+
+ public void setParticipantsHash(byte[] participantsHash) {
+ this.participantsHash = participantsHash;
+ }
+}
+*/
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerSettingImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerSettingImpl.java
new file mode 100644
index 00000000..1af4b7a5
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerSettingImpl.java
@@ -0,0 +1,30 @@
+//package test.com.jd.blockchain.binaryproto.contract.impl;
+
+//import test.com.jd.blockchain.binaryproto.contract.LedgerSetting;
+//import test.com.jd.blockchain.binaryproto.contract.CryptoSetting;
+//import test.com.jd.blockchain.binaryproto.contract.PrivilegeModelSetting;
+
+/**
+ * Created by zhangshuang3 on 2018/7/30.
+ */
+/*
+public class LedgerSettingImpl implements LedgerSetting {
+ private CryptoSetting cryptoSetting;
+ private PrivilegeModelSetting privilegeModelSetting;
+
+ @Override
+ public CryptoSetting getCryptoSetting() {
+ return this.cryptoSetting;
+ }
+ public void setCryptoSetting(CryptoSetting cryptoSetting) {
+ this.cryptoSetting = cryptoSetting;
+ }
+ @Override
+ public PrivilegeModelSetting getPrivilegesModelSetting() {
+ return this.privilegeModelSetting;
+ }
+ public void setPrivilegesModelSetting(PrivilegeModelSetting privilegeModelSetting) {
+ this.privilegeModelSetting = privilegeModelSetting;
+ }
+}
+*/
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/PrivilegeModelSettingImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/PrivilegeModelSettingImpl.java
new file mode 100644
index 00000000..5c1b8c5b
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/PrivilegeModelSettingImpl.java
@@ -0,0 +1,22 @@
+package test.com.jd.blockchain.binaryproto.contract.impl;
+
+import test.com.jd.blockchain.binaryproto.contract.PrivilegeModelSetting;
+
+/**
+ * Created by zhangshuang3 on 2018/7/30.
+ */
+public class PrivilegeModelSettingImpl implements PrivilegeModelSetting {
+ long latestVersion;
+
+ @Override
+ public long getLatestVersion() {
+ return this.latestVersion;
+ }
+
+ public void setLatestVersion(long latestVersion) {
+ this.latestVersion = latestVersion;
+ }
+
+ //Privilege getPrivilege(long version) {
+ //}
+}
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/RefContractImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/RefContractImpl.java
new file mode 100644
index 00000000..700cbf66
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/RefContractImpl.java
@@ -0,0 +1,32 @@
+//package test.com.jd.blockchain.binaryproto.contract.impl;
+
+//import test.com.jd.blockchain.binaryproto.contract.Address;
+//import test.com.jd.blockchain.binaryproto.contract.RefContract;
+
+/**
+ * Created by zhangshuang3 on 2018/7/11.
+ */
+/*
+public class RefContractImpl implements RefContract {
+ private Address address;
+ private Address address1;
+
+ @Override
+ public Address getAddress() {
+ // TODO Auto-generated method stub
+ return this.address;
+ }
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ @Override
+ public Address getAddress1() {
+ // TODO Auto-generated method stub
+ return this.address1;
+ }
+ public void setAddress1(Address address) {
+ this.address1 = address;
+ }
+}
+*/
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/RefEnumImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/RefEnumImpl.java
new file mode 100644
index 00000000..64bcc30b
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/RefEnumImpl.java
@@ -0,0 +1,22 @@
+package test.com.jd.blockchain.binaryproto.contract.impl;
+
+import test.com.jd.blockchain.binaryproto.contract.Level;
+import test.com.jd.blockchain.binaryproto.contract.RefEnum;
+
+/**
+ * Created by zhangshuang3 on 2018/7/11.
+ */
+public class RefEnumImpl implements RefEnum {
+
+ private Level level;
+
+ @Override
+ public Level getLevel() {
+ // TODO Auto-generated method stub
+ return this.level;
+ }
+ public void setLevel(Level level) {
+ this.level = level;
+ }
+}
+
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/StudentImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/StudentImpl.java
new file mode 100644
index 00000000..5b8cfacd
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/StudentImpl.java
@@ -0,0 +1,108 @@
+//package test.com.jd.blockchain.binaryproto.contract.impl;
+
+//import test.com.jd.blockchain.binaryproto.contract.Address;
+//import test.com.jd.blockchain.binaryproto.contract.Level;
+//import test.com.jd.blockchain.binaryproto.contract.Student;
+//import test.com.jd.blockchain.binaryproto.contract.StudentInvert;
+
+/*
+public class StudentImpl implements Student,StudentInvert {
+
+ private int id;
+ private short age;
+ private String name;
+ private Level level;
+ private Address address;
+ private int[] scores;
+ private String[] features;
+ private byte familyMemberNum;
+ private byte[] familyMemberAges;
+ private long[] familyMemberIds;
+
+
+ @Override
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getName() {
+ // TODO Auto-generated method stub
+ return this.name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public Level getLevel() {
+ // TODO Auto-generated method stub
+ return this.level;
+ }
+ public void setLevel(Level level) {
+ this.level = level;
+ }
+
+ @Override
+ public Address getAddress() {
+ // TODO Auto-generated method stub
+ return this.address;
+ }
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ public short getAge() {
+ return age;
+ }
+
+ public void setAge(short age) {
+ this.age = age;
+ }
+
+ @Override
+ public int[] getScores() {
+ // TODO Auto-generated method stub
+ return this.scores;
+ }
+ public void setScores(int[] scores) {
+ this.scores = scores;
+ }
+
+ @Override
+ public String[] getFeatures() {
+ return this.features;
+ }
+ public void setFeatures(String[] features) {
+ this.features = features;
+ }
+
+ @Override
+ public byte getFamilyMemberNum() {
+ return this.familyMemberNum;
+ }
+ public void setFamilyMemberNum(byte familyMemberNum) {
+ this.familyMemberNum = familyMemberNum;
+ }
+
+ @Override
+ public byte[] getFamilyMemberAges() {
+ return this.familyMemberAges;
+ }
+ public void setFamilyMemberAges(byte[] familyMemberAge) {
+ this.familyMemberAges = familyMemberAge;
+ }
+
+ @Override
+ public long[] getFamilyMemberIds() {
+ return this.familyMemberIds;
+ }
+ public void setFamilyMemberIds(long[] familyMemberId) {
+ this.familyMemberIds = familyMemberId;
+ }
+}
+*/
\ No newline at end of file
diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/UserImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/UserImpl.java
new file mode 100644
index 00000000..09fb3cf1
--- /dev/null
+++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/UserImpl.java
@@ -0,0 +1,51 @@
+//package test.com.jd.blockchain.binaryproto.contract.impl;
+
+//import com.jd.blockchain.binaryproto.DConstructor;
+//import com.jd.blockchain.binaryproto.FieldSetter;
+//import com.jd.blockchain.crypto.asymmetric.PubKey;
+//import my.utils.io.ByteArray;
+//import test.com.jd.blockchain.binaryproto.contract.User;
+
+/**
+ * Created by zhangshuang3 on 2018/7/24.
+ */
+/*
+public class UserImpl implements User {
+ String address;
+ PubKey pubKey;
+ PubKey dataPubKey;
+ ByteArray dataHash;
+ ByteArray privHash;
+
+ @DConstructor(name = "UserImpl")
+ public UserImpl(@FieldSetter(name = "getAddress", type = "String") String address, @FieldSetter(name = "getPubKey",type = "PubKey") PubKey pubKey, @FieldSetter(name = "getDataPubKey",type = "PubKey") PubKey dataPubKey,
+ @FieldSetter(name = "getDataHash", type = "byte[]") byte[] dataHash, @FieldSetter(name = "getPrivilegeHash", type = "byte[]") byte[] privHash) {
+ this.address = address;
+ this.pubKey = pubKey;
+ this.dataPubKey = dataPubKey;
+ this.dataHash = ByteArray.wrap(dataHash);
+ this.privHash = ByteArray.wrap(privHash);
+ }
+
+ @Override
+ public String getAddress() {
+ return address;
+ }
+ @Override
+ public PubKey getPubKey() {
+ return pubKey;
+ }
+ @Override
+ public PubKey getDataPubKey() {
+ return dataPubKey;
+ }
+ @Override
+ public ByteArray getDataHash() {
+ return dataHash;
+ }
+ @Override
+ public ByteArray getPrivilegeHash() {
+ return privHash;
+ }
+}
+*/
\ No newline at end of file
diff --git a/source/consensus/consensus-bftsmart/pom.xml b/source/consensus/consensus-bftsmart/pom.xml
new file mode 100644
index 00000000..ddf7b73a
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/pom.xml
@@ -0,0 +1,66 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ consensus
+ 0.8.2.RELEASE
+
+ consensus-bftsmart
+
+
+
+ com.jd.blockchain
+ bft-smart
+
+
+ com.jd.blockchain
+ base
+ ${project.version}
+
+
+ com.jd.blockchain
+ consensus-framework
+ ${project.version}
+
+
+ com.jd.blockchain
+ utils-common
+ ${project.version}
+
+
+ org.apache.commons
+ commons-pool2
+
+
+ com.jd.blockchain
+ ledger-model
+ ${project.version}
+
+
+ com.jd.blockchain
+ tools-keygen
+ ${project.version}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartClientIncomingConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartClientIncomingConfig.java
new file mode 100644
index 00000000..d8e7c115
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartClientIncomingConfig.java
@@ -0,0 +1,67 @@
+package com.jd.blockchain.consensus.bftsmart;
+
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+
+public class BftsmartClientIncomingConfig implements BftsmartClientIncomingSettings {
+
+ private BftsmartConsensusSettings consensusSettings;
+
+ private byte[] topology;
+
+ private byte[] tomConfig;
+
+ private int clientId;
+
+ private PubKey pubKey;
+
+
+ @Override
+ public BftsmartConsensusSettings getConsensusSettings() {
+ return consensusSettings;
+ }
+
+ public void setConsensusSettings(BftsmartConsensusSettings consensusSettings) {
+ this.consensusSettings = consensusSettings;
+ }
+
+ @Override
+ public byte[] getTopology() {
+ return topology;
+ }
+
+ public void setTopology(byte[] topology) {
+ this.topology = topology;
+ }
+
+ @Override
+ public int getClientId() {
+ return clientId;
+ }
+
+ @Override
+ public String getProviderName() {
+ return BftsmartConsensusProvider.NAME;
+ }
+
+ public void setClientId(int clientId) {
+ this.clientId = clientId;
+ }
+
+ @Override
+ public byte[] getTomConfig() {
+ return tomConfig;
+ }
+
+ public void setTomConfig(byte[] tomConfig) {
+ this.tomConfig = tomConfig;
+ }
+
+ @Override
+ public PubKey getPubKey() {
+ return pubKey;
+ }
+
+ public void setPubKey(PubKey pubKey) {
+ this.pubKey = pubKey;
+ }
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartClientIncomingSettings.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartClientIncomingSettings.java
new file mode 100644
index 00000000..9a1548b6
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartClientIncomingSettings.java
@@ -0,0 +1,22 @@
+package com.jd.blockchain.consensus.bftsmart;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.consensus.ClientIncomingSettings;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.utils.ValueType;
+
+@DataContract(code = TypeCodes.CONSENSUS_BFTSMART_CLI_INCOMING_SETTINGS)
+public interface BftsmartClientIncomingSettings extends ClientIncomingSettings {
+
+ @DataField(order = 1, primitiveType = ValueType.BYTES)
+ byte[] getTopology();
+
+ @DataField(order = 2, primitiveType = ValueType.BYTES)
+ byte[] getTomConfig();
+
+ @DataField(order = 3, primitiveType=ValueType.BYTES)
+ PubKey getPubKey();
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartCommitBlockConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartCommitBlockConfig.java
new file mode 100644
index 00000000..23f682ce
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartCommitBlockConfig.java
@@ -0,0 +1,35 @@
+package com.jd.blockchain.consensus.bftsmart;
+
+public class BftsmartCommitBlockConfig implements BftsmartCommitBlockSettings {
+
+ private int txSizePerBlock;
+
+ private long maxDelayMilliSecondsPerBlock;
+
+
+ public BftsmartCommitBlockConfig() {
+
+ }
+
+ public BftsmartCommitBlockConfig(int txSizePerBlock, long maxDelayMilliSecondsPerBlock) {
+ this.txSizePerBlock = txSizePerBlock;
+ this.maxDelayMilliSecondsPerBlock = maxDelayMilliSecondsPerBlock;
+ }
+ @Override
+ public int getTxSizePerBlock() {
+ return txSizePerBlock;
+ }
+
+ public void setTxSizePerBlock(int txSizePerBlock) {
+ this.txSizePerBlock = txSizePerBlock;
+ }
+
+ @Override
+ public long getMaxDelayMilliSecondsPerBlock() {
+ return maxDelayMilliSecondsPerBlock;
+ }
+
+ public void setMaxDelayMilliSecondsPerBlock(long maxDelayMilliSecondsPerBlock) {
+ this.maxDelayMilliSecondsPerBlock = maxDelayMilliSecondsPerBlock;
+ }
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartCommitBlockSettings.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartCommitBlockSettings.java
new file mode 100644
index 00000000..749fdaed
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartCommitBlockSettings.java
@@ -0,0 +1,17 @@
+package com.jd.blockchain.consensus.bftsmart;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.ValueType;
+
+
+@DataContract(code = TypeCodes.CONSENSUS_BFTSMART_BLOCK_SETTINGS)
+public interface BftsmartCommitBlockSettings {
+
+ @DataField(order = 0, primitiveType = ValueType.INT32)
+ int getTxSizePerBlock();
+
+ @DataField(order = 1, primitiveType = ValueType.INT64)
+ long getMaxDelayMilliSecondsPerBlock();
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusConfig.java
new file mode 100644
index 00000000..ca3a301e
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusConfig.java
@@ -0,0 +1,52 @@
+package com.jd.blockchain.consensus.bftsmart;
+
+import com.jd.blockchain.binaryproto.DataContractRegistry;
+import com.jd.blockchain.utils.Property;
+
+public class BftsmartConsensusConfig implements BftsmartConsensusSettings {
+
+ private Property[] bftsmartSystemConfig;
+
+ private BftsmartNodeSettings[] nodes;
+
+ private BftsmartCommitBlockSettings commitBlockSettings;
+
+ static {
+ DataContractRegistry.register(BftsmartConsensusSettings.class);
+ }
+ /**
+ * 创建 bftsmart 共识配置;
+ *
+ * @param nodes
+ * 节点列表
+ * @param commitBlockSettings
+ * 结块设置;
+ * @param bftsmartSystemConfigs
+ * bftsmart系统配置;
+ */
+ public BftsmartConsensusConfig(BftsmartNodeSettings[] nodes, BftsmartCommitBlockSettings commitBlockSettings, Property[] bftsmartSystemConfigs) {
+ this.nodes = nodes;
+ this.commitBlockSettings = commitBlockSettings;
+ this.bftsmartSystemConfig = bftsmartSystemConfigs;
+ }
+
+ @Override
+ public BftsmartNodeSettings[] getNodes() {
+ return nodes;
+ }
+
+ @Override
+ public Property[] getSystemConfigs() {
+ return bftsmartSystemConfig;
+ }
+
+ @Override
+ public BftsmartCommitBlockSettings getCommitBlockSettings() {
+ return commitBlockSettings;
+ }
+
+
+ public void setCommitBlockSettings(BftsmartCommitBlockSettings commitBlockSettings) {
+ this.commitBlockSettings = commitBlockSettings;
+ }
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusProvider.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusProvider.java
new file mode 100644
index 00000000..a8290286
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusProvider.java
@@ -0,0 +1,42 @@
+package com.jd.blockchain.consensus.bftsmart;
+
+import com.jd.blockchain.consensus.ConsensusProvider;
+import com.jd.blockchain.consensus.SettingsFactory;
+import com.jd.blockchain.consensus.bftsmart.client.BftsmartConsensusClientFactory;
+import com.jd.blockchain.consensus.bftsmart.service.BftsmartNodeServerFactory;
+import com.jd.blockchain.consensus.client.ClientFactory;
+import com.jd.blockchain.consensus.service.NodeServerFactory;
+
+public class BftsmartConsensusProvider implements ConsensusProvider {
+
+ public static final String NAME = BftsmartConsensusProvider.class.getName();
+
+ private static BftsmartSettingsFactory settingsFactory = new BftsmartSettingsFactory();
+
+ private static BftsmartConsensusClientFactory clientFactory = new BftsmartConsensusClientFactory();
+
+ private static BftsmartNodeServerFactory nodeServerFactory = new BftsmartNodeServerFactory();
+
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public SettingsFactory getSettingsFactory() {
+ return settingsFactory;
+ }
+
+ @Override
+ public ClientFactory getClientFactory() {
+ return clientFactory;
+ }
+
+ @Override
+ public NodeServerFactory getServerFactory() {
+ return nodeServerFactory;
+ }
+
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusSettings.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusSettings.java
new file mode 100644
index 00000000..ae72383e
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusSettings.java
@@ -0,0 +1,20 @@
+package com.jd.blockchain.consensus.bftsmart;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.utils.Property;
+import com.jd.blockchain.utils.ValueType;
+import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils;
+
+@DataContract(code = TypeCodes.CONSENSUS_BFTSMART_SETTINGS)
+public interface BftsmartConsensusSettings extends ConsensusSettings {
+
+ @DataField(order = 1, primitiveType = ValueType.BYTES, list=true)
+ Property[] getSystemConfigs();
+
+ @DataField(order = 2, refContract = true)
+ BftsmartCommitBlockSettings getCommitBlockSettings();
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusSettingsBuilder.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusSettingsBuilder.java
new file mode 100644
index 00000000..6c25f0ac
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusSettingsBuilder.java
@@ -0,0 +1,213 @@
+package com.jd.blockchain.consensus.bftsmart;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import com.jd.blockchain.tools.keygen.KeyGenCommand;
+import com.jd.blockchain.utils.PropertiesUtils;
+import com.jd.blockchain.utils.codec.Base58Utils;
+import com.jd.blockchain.utils.io.BytesUtils;
+import com.jd.blockchain.utils.net.NetworkAddress;
+
+import org.springframework.core.io.ClassPathResource;
+
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.consensus.ConsensusSettingsBuilder;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+
+public class BftsmartConsensusSettingsBuilder implements ConsensusSettingsBuilder {
+
+ private static final int DEFAULT_TXSIZE = 1000;
+
+ private static final int DEFAULT_MAXDELAY = 1000;
+
+ private static final String CONFIG_TEMPLATE_FILE = "bftsmart.config";
+
+ /**
+ * 参数键:节点数量;
+ */
+ public static final String SERVER_NUM_KEY = "system.servers.num";
+
+ /**
+ * 参数键:结块条件设置;
+ */
+ public static final String BFTSMART_BLOCK_TXSIZE_KEY = "system.block.txsize";
+
+ public static final String BFTSMART_BLOCK_MAXDELAY_KEY = "system.block.maxdelay";
+
+ // /**
+ // * 参数键格式:节点地址;
+ // */
+ // public static final String ADDRESS_PATTERN = "node.%s.address";
+
+ /**
+ * 参数键格式:节点公钥;
+ */
+ public static final String PUBKEY_PATTERN = "system.server.%s.pubkey";
+
+ /**
+ * 参数键格式:节点共识服务的网络地址;
+ */
+ public static final String CONSENSUS_HOST_PATTERN = "system.server.%s.network.host";
+
+ /**
+ * 参数键格式:节点共识服务的端口;
+ */
+ public static final String CONSENSUS_PORT_PATTERN = "system.server.%s.network.port";
+
+ /**
+ * 参数键格式:节点共识服务的通讯是否开启安全选项;
+ */
+ public static final String CONSENSUS_SECURE_PATTERN = "system.server.%s.network.secure";
+
+ private static Properties CONFIG_TEMPLATE;
+ static {
+ ClassPathResource configResource = new ClassPathResource(CONFIG_TEMPLATE_FILE);
+ try {
+ try (InputStream in = configResource.getInputStream()) {
+ CONFIG_TEMPLATE = PropertiesUtils.load(in, BytesUtils.DEFAULT_CHARSET);
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ //解析得到结块的相关配置信息
+ public BftsmartCommitBlockConfig createBlockConfig(Properties resolvingProps) {
+ BftsmartCommitBlockConfig blockConfig = new BftsmartCommitBlockConfig();
+
+ String txSizeString = PropertiesUtils.getRequiredProperty(resolvingProps, BFTSMART_BLOCK_TXSIZE_KEY);
+ resolvingProps.remove(BFTSMART_BLOCK_TXSIZE_KEY);
+
+ if (txSizeString == null || txSizeString.length() == 0) {
+ blockConfig.setTxSizePerBlock(DEFAULT_TXSIZE);
+ }
+ else {
+ blockConfig.setTxSizePerBlock(Integer.parseInt(txSizeString));
+ }
+
+ String maxDelayString = PropertiesUtils.getRequiredProperty(resolvingProps, BFTSMART_BLOCK_MAXDELAY_KEY);
+ resolvingProps.remove(BFTSMART_BLOCK_MAXDELAY_KEY);
+
+ if (maxDelayString == null || maxDelayString.length() == 0) {
+ blockConfig.setMaxDelayMilliSecondsPerBlock(DEFAULT_MAXDELAY);
+ }
+ else {
+ blockConfig.setMaxDelayMilliSecondsPerBlock(Long.parseLong(maxDelayString));
+ }
+
+ return blockConfig;
+ }
+
+ @Override
+ public Properties createPropertiesTemplate() {
+ return PropertiesUtils.cloneFrom(CONFIG_TEMPLATE);
+ }
+
+ @Override
+ public BftsmartConsensusSettings createSettings(Properties props) {
+ Properties resolvingProps = PropertiesUtils.cloneFrom(props);
+ int serversNum = PropertiesUtils.getInt(resolvingProps, SERVER_NUM_KEY);
+ if (serversNum < 0) {
+ throw new IllegalArgumentException(String.format("Property[%s] is negative!", SERVER_NUM_KEY));
+ }
+ if (serversNum < 4) {
+ throw new IllegalArgumentException(String.format("Property[%s] is less than 4!", SERVER_NUM_KEY));
+ }
+
+ BftsmartCommitBlockConfig blockConfig = createBlockConfig(resolvingProps);
+
+ BftsmartNodeSettings[] nodesSettings = new BftsmartNodeSettings[serversNum];
+ for (int i = 0; i < serversNum; i++) {
+ int id = i;
+
+ String keyOfPubkey = keyOfNode(PUBKEY_PATTERN, id);
+ String base58PubKey = PropertiesUtils.getRequiredProperty(resolvingProps, keyOfPubkey);
+// PubKey pubKey = new PubKey(Base58Utils.decode(base58PubKey));
+ PubKey pubKey = KeyGenCommand.decodePubKey(base58PubKey);
+ resolvingProps.remove(keyOfPubkey);
+
+ String keyOfHost = keyOfNode(CONSENSUS_HOST_PATTERN, id);
+ String networkAddressHost = PropertiesUtils.getRequiredProperty(resolvingProps, keyOfHost);
+ resolvingProps.remove(keyOfHost);
+
+ String keyOfPort = keyOfNode(CONSENSUS_PORT_PATTERN, id);
+ int networkAddressPort = PropertiesUtils.getInt(resolvingProps, keyOfPort);
+ resolvingProps.remove(keyOfPort);
+
+ String keyOfSecure = keyOfNode(CONSENSUS_SECURE_PATTERN, id);
+ boolean networkAddressSecure = PropertiesUtils.getBoolean(resolvingProps, keyOfSecure);
+ resolvingProps.remove(keyOfSecure);
+
+ BftsmartNodeConfig nodeConfig = new BftsmartNodeConfig(pubKey, id,
+ new NetworkAddress(networkAddressHost, networkAddressPort, networkAddressSecure));
+ nodesSettings[i] = nodeConfig;
+ }
+
+ BftsmartConsensusConfig config = new BftsmartConsensusConfig(nodesSettings, blockConfig,
+ PropertiesUtils.getOrderedValues(resolvingProps));
+ return config;
+ }
+
+ private static String keyOfNode(String pattern, int id) {
+ return String.format(pattern, id);
+ }
+
+ @Override
+ public void writeSettings(ConsensusSettings settings, Properties props) {
+ int serversNum = PropertiesUtils.getInt(props, SERVER_NUM_KEY);
+ if (serversNum > 0) {
+ for (int i = 0; i < serversNum; i++) {
+ int id = i;
+ String keyOfPubkey = keyOfNode(PUBKEY_PATTERN, id);
+ props.remove(keyOfPubkey);
+
+ String keyOfHost = keyOfNode(CONSENSUS_HOST_PATTERN, id);
+ props.remove(keyOfHost);
+
+ String keyOfPort = keyOfNode(CONSENSUS_PORT_PATTERN, id);
+ props.remove(keyOfPort);
+
+ String keyOfSecure = keyOfNode(CONSENSUS_SECURE_PATTERN, id);
+ props.remove(keyOfSecure);
+ }
+ }
+
+ BftsmartConsensusSettings bftsmartSettings = (BftsmartConsensusSettings) settings;
+ BftsmartNodeSettings[] nodesSettings = (BftsmartNodeSettings[]) bftsmartSettings.getNodes();
+ serversNum = nodesSettings.length;
+ props.setProperty(SERVER_NUM_KEY, serversNum + "");
+
+ //获得结块相关的属性信息
+ BftsmartCommitBlockSettings blockSettings = bftsmartSettings.getCommitBlockSettings();
+ if (blockSettings == null) {
+ props.setProperty(BFTSMART_BLOCK_TXSIZE_KEY, DEFAULT_TXSIZE + "");
+ props.setProperty(BFTSMART_BLOCK_MAXDELAY_KEY, DEFAULT_MAXDELAY + "");
+ } else {
+ int txSize = blockSettings.getTxSizePerBlock();
+ long maxDelay = blockSettings.getMaxDelayMilliSecondsPerBlock();
+ props.setProperty(BFTSMART_BLOCK_TXSIZE_KEY, txSize + "");
+ props.setProperty(BFTSMART_BLOCK_MAXDELAY_KEY, maxDelay + "");
+ }
+
+ for (int i = 0; i < serversNum; i++) {
+ BftsmartNodeSettings ns = nodesSettings[i];
+ int id = i;
+ String keyOfPubkey = keyOfNode(PUBKEY_PATTERN, id);
+ props.setProperty(keyOfPubkey, ns.getPubKey().toBase58());
+
+ String keyOfHost = keyOfNode(CONSENSUS_HOST_PATTERN, id);
+ props.setProperty(keyOfHost, ns.getNetworkAddress() == null ? "" : ns.getNetworkAddress().getHost());
+
+ String keyOfPort = keyOfNode(CONSENSUS_PORT_PATTERN, id);
+ props.setProperty(keyOfPort, ns.getNetworkAddress() == null ? "" : ns.getNetworkAddress().getPort() + "");
+
+ String keyOfSecure = keyOfNode(CONSENSUS_SECURE_PATTERN, id);
+ props.setProperty(keyOfSecure, ns.getNetworkAddress() == null ? "false" : ns.getNetworkAddress().isSecure() + "");
+ }
+
+ PropertiesUtils.setValues(props, bftsmartSettings.getSystemConfigs());
+ }
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartNodeConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartNodeConfig.java
new file mode 100644
index 00000000..005e05c5
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartNodeConfig.java
@@ -0,0 +1,66 @@
+package com.jd.blockchain.consensus.bftsmart;
+
+import com.jd.blockchain.binaryproto.DataContractRegistry;
+import com.jd.blockchain.crypto.AddressEncoding;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.utils.net.NetworkAddress;
+
+public class BftsmartNodeConfig implements BftsmartNodeSettings {
+
+ private int id;
+
+ private String address;
+
+ private PubKey pubKey;
+
+ private NetworkAddress networkAddress;
+
+ public BftsmartNodeConfig() {
+ }
+
+ static {
+ DataContractRegistry.register(BftsmartNodeSettings.class);
+ }
+
+ public BftsmartNodeConfig(PubKey pubKey, int id, NetworkAddress networkAddress) {
+ this.address = AddressEncoding.generateAddress(pubKey).toBase58();
+ this.pubKey = pubKey;
+ this.id = id;
+ this.networkAddress = networkAddress;
+ }
+
+ @Override
+ public String getAddress() {
+ return address;
+ }
+
+ @Override
+ public int getId() {
+ return id;
+ }
+
+ @Override
+ public NetworkAddress getNetworkAddress() {
+ return networkAddress;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public void setNetworkAddress(NetworkAddress networkAddress) {
+ this.networkAddress = networkAddress;
+ }
+
+ public PubKey getPubKey() {
+ return pubKey;
+ }
+
+ public void setPubKey(PubKey pubKey) {
+ this.pubKey = pubKey;
+ }
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartNodeSettings.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartNodeSettings.java
new file mode 100644
index 00000000..50a5e1a1
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartNodeSettings.java
@@ -0,0 +1,45 @@
+package com.jd.blockchain.consensus.bftsmart;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.consensus.NodeSettings;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.utils.ValueType;
+import com.jd.blockchain.utils.net.NetworkAddress;
+
+@DataContract(code = TypeCodes.CONSENSUS_BFTSMART_NODE_SETTINGS)
+public interface BftsmartNodeSettings extends NodeSettings {
+
+ /**
+ * 节点所属的参与方的区块链地址;
+ */
+// @DataField(order = 0, primitiveType = ValueType.TEXT)
+// @Override
+// String getAddress();
+
+ /**
+ * Base58 格式的公钥;
+ *
+ * @return
+ */
+// @DataField(order = 1, primitiveType = ValueType.BYTES)
+// PubKey getPubKey();
+
+ /**
+ * 节点的ID;
+ *
+ * @return
+ */
+ @DataField(order = 2, primitiveType = ValueType.INT32)
+ int getId();
+
+ /**
+ * 共识协议的网络地址;
+ *
+ * @return
+ */
+ @DataField(order = 3, primitiveType = ValueType.BYTES)
+ NetworkAddress getNetworkAddress();
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartSettingsFactory.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartSettingsFactory.java
new file mode 100644
index 00000000..45be9423
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartSettingsFactory.java
@@ -0,0 +1,73 @@
+package com.jd.blockchain.consensus.bftsmart;
+
+import com.jd.blockchain.binaryproto.BinaryEncodingUtils;
+import com.jd.blockchain.binaryproto.DataContractRegistry;
+import com.jd.blockchain.consensus.*;
+import com.jd.blockchain.utils.io.BytesEncoder;
+
+public class BftsmartSettingsFactory implements SettingsFactory {
+
+ private static ConsensusSettingsEncoder CS_ENCODER = new ConsensusSettingsEncoder();
+
+ private static ClientIncomingSettingsEncoder CI_ENCODER =new ClientIncomingSettingsEncoder();
+
+ static {
+ DataContractRegistry.register(BftsmartConsensusSettings.class);
+
+ DataContractRegistry.register(BftsmartClientIncomingSettings.class);
+ }
+
+ @Override
+ public BftsmartConsensusSettingsBuilder getConsensusSettingsBuilder() {
+
+ return new BftsmartConsensusSettingsBuilder();
+ }
+
+ @Override
+ public BytesEncoder getConsensusSettingsEncoder() {
+ return CS_ENCODER;
+ }
+
+ @Override
+ public BytesEncoder getIncomingSettingsEncoder() {
+ return CI_ENCODER;
+ }
+
+
+
+ private static class ConsensusSettingsEncoder implements BytesEncoder{
+
+ @Override
+ public byte[] encode(ConsensusSettings data) {
+ if (data instanceof BftsmartConsensusSettings) {
+ return BinaryEncodingUtils.encode(data, BftsmartConsensusSettings.class);
+ }
+ throw new IllegalArgumentException("Settings data isn't supported! Accept BftsmartConsensusSettings only!");
+ }
+
+ @Override
+ public ConsensusSettings decode(byte[] bytes) {
+ return BinaryEncodingUtils.decodeAs(bytes, BftsmartConsensusSettings.class);
+ }
+
+ }
+
+ private static class ClientIncomingSettingsEncoder implements BytesEncoder{
+
+ @Override
+ public byte[] encode(ClientIncomingSettings data) {
+ if (data instanceof BftsmartClientIncomingSettings) {
+ return BinaryEncodingUtils.encode(data, BftsmartClientIncomingSettings.class);
+ }
+ throw new IllegalArgumentException("Settings data isn't supported! Accept BftsmartClientIncomingSettings only!");
+ }
+
+ @Override
+ public ClientIncomingSettings decode(byte[] bytes) {
+ return BinaryEncodingUtils.decodeAs(bytes, BftsmartClientIncomingSettings.class);
+ }
+
+ }
+
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartTopology.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartTopology.java
new file mode 100644
index 00000000..f6832fb8
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartTopology.java
@@ -0,0 +1,30 @@
+package com.jd.blockchain.consensus.bftsmart;
+
+import bftsmart.reconfiguration.views.View;
+import com.jd.blockchain.consensus.Topology;
+import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils;
+
+public class BftsmartTopology implements Topology {
+
+ private static final long serialVersionUID = -3042599438265726240L;
+
+ private View view;
+
+ public BftsmartTopology(View view){
+ this.view = view;
+ }
+
+ @Override
+ public int getId() {
+ return view.getId();
+ }
+
+ @Override
+ public Topology copyOf() {
+ return BinarySerializeUtils.copyOf(this);
+ }
+
+ public View getView() {
+ return view;
+ }
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartTransactionType.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartTransactionType.java
new file mode 100644
index 00000000..bf42510f
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartTransactionType.java
@@ -0,0 +1,16 @@
+//package com.jd.blockchain.consensus.bftsmart;
+//
+//public enum BftsmartTransactionType {
+// TRANSACTION((int)0),
+// COMMITBLOCK((int)1);
+//
+// public final int CODE;
+//
+// public int getCode() {
+// return CODE;
+// }
+// private BftsmartTransactionType(int code) {
+// this.CODE = code;
+// }
+//
+//}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientConfig.java
new file mode 100644
index 00000000..6bb9e238
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientConfig.java
@@ -0,0 +1,79 @@
+package com.jd.blockchain.consensus.bftsmart.client;
+
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.consensus.bftsmart.BftsmartClientIncomingSettings;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+
+
+public class BftsmartClientConfig implements BftsmartClientSettings {
+
+ private int clientId;
+ private PubKey clientPubkey;
+ private ConsensusSettings consensusSettings;
+ private byte[] topology;
+ private byte[] tomConfig;
+ BftsmartClientIncomingSettings clientIncomingSettings;
+
+ public BftsmartClientConfig(int clientId, PubKey clientPubkey, ConsensusSettings consensusSettings, byte[] topology, byte[] tomConfig) {
+ this.clientId = clientId;
+ this.clientPubkey = clientPubkey;
+ this.consensusSettings = consensusSettings;
+ this.topology = topology;
+ this.tomConfig = tomConfig;
+ }
+
+ public BftsmartClientConfig(BftsmartClientIncomingSettings clientIncomingSettings) {
+ this.clientIncomingSettings = clientIncomingSettings;
+ this.clientId = clientIncomingSettings.getClientId();
+ this.clientPubkey = clientIncomingSettings.getPubKey();
+ this.consensusSettings = clientIncomingSettings.getConsensusSettings();
+ this.topology = clientIncomingSettings.getTopology();
+ this.tomConfig = clientIncomingSettings.getTomConfig();
+
+ }
+ @Override
+ public int getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(int clientId) {
+ this.clientId = clientId;
+ }
+
+
+ @Override
+ public PubKey getClientPubKey() {
+ return clientPubkey;
+ }
+
+ public void setClientPubkey(PubKey clientPubkey) {
+ this.clientPubkey = clientPubkey;
+ }
+
+ @Override
+ public ConsensusSettings getConsensusSettings() {
+ return consensusSettings;
+ }
+
+ public void setConsensusSettings(ConsensusSettings consensusSettings) {
+ this.consensusSettings = consensusSettings;
+ }
+
+ public byte[] getTopology() {
+ return topology;
+ }
+
+ public void setTopology(byte[] topology) {
+ this.topology = topology;
+ }
+
+ @Override
+ public byte[] getTomConfig() {
+ return tomConfig;
+ }
+
+ public void setTomConfig(byte[] tomConfig) {
+ this.tomConfig = tomConfig;
+ }
+}
+
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientIdentification.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientIdentification.java
new file mode 100644
index 00000000..3b046e1f
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientIdentification.java
@@ -0,0 +1,60 @@
+package com.jd.blockchain.consensus.bftsmart.client;
+
+import com.jd.blockchain.consensus.ClientIdentification;
+import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider;
+import com.jd.blockchain.crypto.CryptoUtils;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.asymmetric.SignatureDigest;
+import com.jd.blockchain.crypto.asymmetric.SignatureFunction;
+
+import java.util.Arrays;
+
+public class BftsmartClientIdentification implements ClientIdentification {
+
+ private byte[] identityInfo;
+ private PubKey pubKey;
+ private SignatureDigest signatureDigest;
+
+ public BftsmartClientIdentification() {
+
+ }
+
+ public BftsmartClientIdentification(ClientIdentification clientIdentification) {
+ identityInfo = clientIdentification.getIdentityInfo();
+ pubKey = clientIdentification.getPubKey();
+ signatureDigest = clientIdentification.getSignature();
+ }
+
+ @Override
+ public byte[] getIdentityInfo() {
+ return identityInfo;
+ }
+
+ public void setIdentityInfo(byte[] identityInfo) {
+ this.identityInfo = identityInfo;
+ }
+
+ @Override
+ public PubKey getPubKey() {
+ return pubKey;
+ }
+
+ public void setPubKey(PubKey pubKey) {
+ this.pubKey = pubKey;
+ }
+
+ @Override
+ public SignatureDigest getSignature() {
+ return signatureDigest;
+ }
+
+ @Override
+ public String getProviderName() {
+ return BftsmartConsensusProvider.NAME;
+ }
+
+ public void setSignatureDigest(SignatureDigest signatureDigest) {
+ this.signatureDigest = signatureDigest;
+ }
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientSettings.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientSettings.java
new file mode 100644
index 00000000..94bf3d53
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientSettings.java
@@ -0,0 +1,12 @@
+package com.jd.blockchain.consensus.bftsmart.client;
+
+import com.jd.blockchain.consensus.client.ClientSettings;
+
+
+public interface BftsmartClientSettings extends ClientSettings {
+
+ byte[] getTopology();
+
+ byte[] getTomConfig();
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartConsensusClient.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartConsensusClient.java
new file mode 100644
index 00000000..25a995c7
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartConsensusClient.java
@@ -0,0 +1,131 @@
+package com.jd.blockchain.consensus.bftsmart.client;
+
+import com.jd.blockchain.consensus.MessageService;
+import com.jd.blockchain.consensus.client.ClientSettings;
+import com.jd.blockchain.consensus.client.ConsensusClient;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class BftsmartConsensusClient implements ConsensusClient {
+
+
+ private final AtomicInteger addId = new AtomicInteger();
+
+ private BftsmartPeerProxyPool asyncPeerProxyPool;
+
+ private int gatewayId;
+
+ private ClientSettings clientSettings;
+
+ public BftsmartConsensusClient(ClientSettings clientSettings) {
+
+ this.clientSettings = clientSettings;
+ this.gatewayId = clientSettings.getClientId();
+
+ connect();
+ }
+
+
+ public BftsmartPeerProxyPool getConsensusClientPool() {
+ return this.asyncPeerProxyPool;
+ }
+
+ @Override
+ public MessageService getMessageService() {
+ return new BftsmartMessageService(asyncPeerProxyPool);
+ }
+
+ @Override
+ public ClientSettings getSettings() {
+ return clientSettings;
+ }
+
+ @Override
+ public boolean isConnected() {
+ return this.asyncPeerProxyPool != null;
+ }
+
+ @Override
+ public void connect() {
+
+ //consensus client pool
+ BftsmartPeerProxyFactory peerProxyFactory = new BftsmartPeerProxyFactory((BftsmartClientSettings)clientSettings, gatewayId);
+ this.asyncPeerProxyPool = new BftsmartPeerProxyPool(peerProxyFactory);
+
+// MemoryBasedViewStorage viewStorage = new MemoryBasedViewStorage(((BftsmartClientSettings)clientSettings).getTopology().getView());
+// TOMConfiguration tomConfiguration = ((BftsmartConsensusConfig)clientSettings.getConsensusSettings()).getBftsmartConfig();
+//
+// //by serialize keep origin tom config
+// byte[] tomBytes = BinarySerializeUtils.serialize(tomConfiguration);
+// TOMConfiguration decodeTom = BinarySerializeUtils.deserialize(tomBytes);
+//
+// int clientId = gatewayId *100 + addId.incrementAndGet();
+//
+// //every proxy client has unique id;
+// decodeTom.setProcessId(clientId);
+// this.peerProxy = new AsynchServiceProxy(decodeTom, viewStorage);
+
+ }
+
+ @Override
+ public void close() {
+ if (asyncPeerProxyPool != null) {
+ asyncPeerProxyPool.close();
+ }
+ }
+
+// public void asyncSendOrdered(byte[] message, AsyncCallback callback) {
+// AsyncReplier replier = new AsyncReplier(callback, peerProxy);
+// peerProxy.invokeAsynchRequest(message, replier, TOMMessageType.ORDERED_REQUEST);
+// }
+
+// private static class AsyncReplier implements ReplyListener {
+//
+// private AsynchServiceProxy peerProxy;
+//
+// private AtomicInteger replies = new AtomicInteger(0);
+//
+// private AsyncCallback messageHandle;
+//
+// public AsyncReplier(AsyncCallback messageHandle, AsynchServiceProxy peerProxy) {
+// this.messageHandle = messageHandle;
+// this.peerProxy = peerProxy;
+// }
+//
+// @Override
+// public void reset() {
+// replies.set(0);
+// }
+//
+// @Override
+// public void replyReceived(RequestContext context, TOMMessage reply) {
+// int replyCount = replies.incrementAndGet();
+//
+// double q = Math.ceil((double) (peerProxy.getViewManager().getCurrentViewN()
+// + peerProxy.getViewManager().getCurrentViewF() + 1) / 2.0);
+//
+// if (replyCount >= q) {
+// peerProxy.cleanAsynchRequest(context.getOperationId());
+// messageHandle.complete(reply.getContent(), null);
+// }
+// }
+//
+// }
+
+// private static class BftsmartAsyncFuture extends CompletableAsyncFuture {
+// @Override
+// public void setSuccess(T value) {
+// super.setSuccess(value);
+// }
+//
+// @Override
+// public void setError(Throwable ex) {
+// super.setError(ex);
+// }
+//
+// @Override
+// public void setError(String errorCode) {
+// super.setError(errorCode);
+// }
+// }
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartConsensusClientFactory.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartConsensusClientFactory.java
new file mode 100644
index 00000000..51afe4d2
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartConsensusClientFactory.java
@@ -0,0 +1,137 @@
+package com.jd.blockchain.consensus.bftsmart.client;
+
+import bftsmart.reconfiguration.util.TOMConfiguration;
+import com.jd.blockchain.consensus.ClientIdentification;
+import com.jd.blockchain.consensus.ClientIncomingSettings;
+import com.jd.blockchain.consensus.ConsensusManageService;
+import com.jd.blockchain.consensus.ConsensusSecurityException;
+import com.jd.blockchain.consensus.bftsmart.BftsmartClientIncomingSettings;
+import com.jd.blockchain.consensus.bftsmart.BftsmartTopology;
+import com.jd.blockchain.consensus.bftsmart.service.BftsmartConsensusManageService;
+import com.jd.blockchain.consensus.client.ClientFactory;
+import com.jd.blockchain.consensus.client.ClientSettings;
+import com.jd.blockchain.consensus.client.ConsensusClient;
+import com.jd.blockchain.crypto.CryptoUtils;
+import com.jd.blockchain.crypto.asymmetric.*;
+import com.jd.blockchain.utils.http.agent.HttpServiceAgent;
+import com.jd.blockchain.utils.http.agent.ServiceEndpoint;
+import com.jd.blockchain.utils.net.NetworkAddress;
+import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class BftsmartConsensusClientFactory implements ClientFactory {
+
+
+ private AtomicInteger addId = new AtomicInteger();
+
+ private String localDomain = "localhost";
+ private String localIp = "127.0.0.1";
+
+ public BftsmartConsensusClientFactory() {
+
+ }
+
+
+ @Override
+ public BftsmartClientIdentification buildAuthId(CryptoKeyPair clientKeyPair) {
+
+ PubKey pubKey = clientKeyPair.getPubKey();
+ PrivKey privKey = clientKeyPair.getPrivKey();
+
+ SignatureFunction signatureFunction = CryptoUtils.sign(pubKey.getAlgorithm());
+ SignatureDigest signatureDigest = signatureFunction.sign(privKey, pubKey.toBytes());
+
+ BftsmartClientIdentification bftsmartClientIdentification = new BftsmartClientIdentification();
+ bftsmartClientIdentification.setIdentityInfo(pubKey.toBytes());
+ bftsmartClientIdentification.setPubKey(pubKey);
+ bftsmartClientIdentification.setSignatureDigest(signatureDigest);
+
+ return bftsmartClientIdentification;
+ }
+
+ @Override
+ public ClientSettings buildClientSettings(ClientIncomingSettings incomingSettings) {
+
+ BftsmartClientIncomingSettings clientIncomingSettings = (BftsmartClientIncomingSettings) incomingSettings;
+
+ BftsmartClientSettings clientSettings = new BftsmartClientConfig(clientIncomingSettings);
+
+ return clientSettings;
+ }
+
+ @Override
+ public ConsensusClient setupClient(ClientSettings settings) {
+
+ return new BftsmartConsensusClient(settings);
+ }
+
+ @Override
+ public ConsensusManageService createManageServiceClient(String[] serviceNodes) {
+// BftsmartConsensusManageService consensusManageService = null;
+// BftsmartClientIncomingSettings clientIncomingSettings;
+//
+//
+// try {
+// if (serviceNodes == null) {
+// throw new ConsensusSecurityException("createManageServiceClient param error!");
+// }
+//
+// for (int i = 0; i < serviceNodes.length; i++) {
+//
+// NetworkAddress networkAddress = getIpPortFromUrl(serviceNodes[i]);
+// if (networkAddress == null) {
+// continue;
+// }
+// ServiceEndpoint peerServer = new ServiceEndpoint(networkAddress.getHost(), networkAddress.getPort(), false);
+// consensusManageService = HttpServiceAgent.createService(BftsmartConsensusManageService.class, peerServer);
+// clientIncomingSettings = consensusManageService.authClientIncoming(clientIdentification);
+//
+// if (clientIncomingSettings == null) {
+// consensusManageService = null;
+// } else {
+// //认证成功
+// break;
+// }
+// }
+//
+// } catch (Exception e) {
+// e.printStackTrace();
+// }
+
+// return consensusManageService;
+ return null;
+ }
+
+ private NetworkAddress getIpPortFromUrl(String url) {
+
+ // 1.check null
+ if (url == null || url.trim().equals("")) {
+ return null;
+ }
+
+ // 2. localhost replace to 127.0.0.1
+ if(url.startsWith("http://" + localDomain) ){
+ url = url.replace("http://" + localDomain, "http://" + localIp) ;
+ }
+
+ String host = "";
+ Pattern p = Pattern.compile("(?<=//|)((\\w)+\\.)+\\w+(:\\d{0,5})?");
+ Matcher matcher = p.matcher(url);
+ if (matcher.find()) {
+ host = matcher.group() ;
+ }
+
+ if(host.contains(":") == false){
+ //default port :80
+ return new NetworkAddress(host, 80);
+ }
+ else {
+ String[] ipPortArr = host.split(":");
+ return new NetworkAddress(ipPortArr[0], Integer.parseInt(ipPortArr[1]));
+ }
+ }
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartMessageService.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartMessageService.java
new file mode 100644
index 00000000..53a5f0a8
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartMessageService.java
@@ -0,0 +1,73 @@
+package com.jd.blockchain.consensus.bftsmart.client;
+
+import bftsmart.tom.AsynchServiceProxy;
+import com.jd.blockchain.consensus.MessageService;
+import com.jd.blockchain.utils.concurrent.AsyncFuture;
+import com.jd.blockchain.utils.concurrent.CompletableAsyncFuture;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class BftsmartMessageService implements MessageService {
+
+ private BftsmartPeerProxyPool asyncPeerProxyPool;
+
+ public BftsmartMessageService(BftsmartPeerProxyPool peerProxyPool) {
+ this.asyncPeerProxyPool = peerProxyPool;
+ }
+
+ @Override
+ public AsyncFuture sendOrdered(byte[] message) {
+ return sendOrderedMessage(message);
+ }
+
+ private AsyncFuture sendOrderedMessage(byte[] message) {
+ CompletableAsyncFuture asyncFuture = new CompletableAsyncFuture<>();
+ AsynchServiceProxy asynchServiceProxy = null;
+ try {
+ asynchServiceProxy = asyncPeerProxyPool.borrowObject();
+// //0: Transaction msg, 1: Commitblock msg
+// byte[] msgType = BytesUtils.toBytes(0);
+// byte[] wrapMsg = new byte[message.length + 4];
+// System.arraycopy(message, 0, wrapMsg, 4, message.length);
+// System.arraycopy(msgType, 0, wrapMsg, 0, 4);
+//
+// System.out.printf("BftsmartMessageService invokeOrdered time = %s, id = %s threadId = %s \r\n",
+// System.currentTimeMillis(), asynchServiceProxy.getProcessId(), Thread.currentThread().getId());
+
+ byte[] result = asynchServiceProxy.invokeOrdered(message);
+ asyncFuture.complete(result);
+
+ } catch (Exception e) {
+ throw new RuntimeException();
+
+ } finally {
+ asyncPeerProxyPool.returnObject(asynchServiceProxy);
+ }
+
+ return asyncFuture;
+ }
+
+ @Override
+ public AsyncFuture sendUnordered(byte[] message) {
+ return sendUnorderedMessage(message);
+ }
+
+ private AsyncFuture sendUnorderedMessage(byte[] message) {
+ CompletableAsyncFuture asyncFuture = new CompletableAsyncFuture<>();
+ AsynchServiceProxy asynchServiceProxy = null;
+ try {
+ asynchServiceProxy = asyncPeerProxyPool.borrowObject();
+ byte[] result = asynchServiceProxy.invokeUnordered(message);
+ asyncFuture.complete(result);
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ asyncPeerProxyPool.returnObject(asynchServiceProxy);
+ }
+ return asyncFuture;
+ }
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyFactory.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyFactory.java
new file mode 100644
index 00000000..306a71f0
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyFactory.java
@@ -0,0 +1,47 @@
+package com.jd.blockchain.consensus.bftsmart.client;
+
+import bftsmart.reconfiguration.util.TOMConfiguration;
+import bftsmart.reconfiguration.views.MemoryBasedViewStorage;
+import bftsmart.tom.AsynchServiceProxy;
+import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusConfig;
+import com.jd.blockchain.consensus.bftsmart.BftsmartTopology;
+import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils;
+
+import org.apache.commons.pool2.BasePooledObjectFactory;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class BftsmartPeerProxyFactory extends BasePooledObjectFactory {
+
+ private BftsmartClientSettings bftsmartClientSettings;
+
+ private int gatewayId;
+
+ private AtomicInteger index = new AtomicInteger(1);
+
+ public BftsmartPeerProxyFactory(BftsmartClientSettings bftsmartClientSettings, int gatewayId) {
+ this.bftsmartClientSettings = bftsmartClientSettings;
+ this.gatewayId = gatewayId;
+ }
+
+ @Override
+ public AsynchServiceProxy create() throws Exception {
+
+ BftsmartTopology topology = BinarySerializeUtils.deserialize(bftsmartClientSettings.getTopology());
+
+ MemoryBasedViewStorage viewStorage = new MemoryBasedViewStorage(topology.getView());
+ TOMConfiguration tomConfiguration = BinarySerializeUtils.deserialize(bftsmartClientSettings.getTomConfig());
+
+ //every proxy client has unique id;
+ tomConfiguration.setProcessId(gatewayId + index.getAndIncrement());
+ AsynchServiceProxy peerProxy = new AsynchServiceProxy(tomConfiguration, viewStorage);
+ return peerProxy;
+ }
+
+ @Override
+ public PooledObject wrap(AsynchServiceProxy asynchServiceProxy) {
+ return new DefaultPooledObject<>(asynchServiceProxy);
+ }
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyPool.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyPool.java
new file mode 100644
index 00000000..1b5a5f8e
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyPool.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.bft.BftsmartConsensusClientPool
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/10/30 下午6:50
+ * Description:
+ */
+package com.jd.blockchain.consensus.bftsmart.client;
+
+import bftsmart.tom.AsynchServiceProxy;
+import org.apache.commons.pool2.PooledObjectFactory;
+import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+
+public class BftsmartPeerProxyPool extends GenericObjectPool {
+
+ public BftsmartPeerProxyPool(PooledObjectFactory factory) {
+ this(factory, null);
+ }
+
+ public BftsmartPeerProxyPool(PooledObjectFactory factory, GenericObjectPoolConfig config) {
+ super(factory, config == null ? new BftsmartPeerProxyPoolConfig() : config);
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyPoolConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyPoolConfig.java
new file mode 100644
index 00000000..fb0b3884
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyPoolConfig.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.bft.BftsmartConsensusClientPool
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/10/30 下午6:50
+ * Description:
+ */
+package com.jd.blockchain.consensus.bftsmart.client;
+
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+
+public class BftsmartPeerProxyPoolConfig extends GenericObjectPoolConfig {
+
+ public static final int MAX_TOTAL = 100;
+
+ private final int MIN_IDLE = 0;
+
+ private final int MAX_IDLE = 100;
+
+ public BftsmartPeerProxyPoolConfig() {
+ setMaxTotal(MAX_TOTAL);
+ setMinIdle(MIN_IDLE);
+ setMaxIdle(MAX_IDLE);
+ }
+
+ public BftsmartPeerProxyPoolConfig(int maxTotal, int minIdle, int maxIdle) {
+ setMaxTotal(maxTotal);
+ setMinIdle(minIdle);
+ setMaxIdle(maxIdle);
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartConsensusManageService.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartConsensusManageService.java
new file mode 100644
index 00000000..8331a969
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartConsensusManageService.java
@@ -0,0 +1,59 @@
+package com.jd.blockchain.consensus.bftsmart.service;
+
+import com.jd.blockchain.consensus.ClientIdentification;
+import com.jd.blockchain.consensus.ConsensusManageService;
+import com.jd.blockchain.consensus.ConsensusSecurityException;
+import com.jd.blockchain.consensus.bftsmart.BftsmartClientIncomingConfig;
+import com.jd.blockchain.consensus.bftsmart.BftsmartClientIncomingSettings;
+import com.jd.blockchain.crypto.CryptoUtils;
+import com.jd.blockchain.crypto.asymmetric.SignatureFunction;
+import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class BftsmartConsensusManageService implements ConsensusManageService {
+
+ public static final int CLIENT_RANGE = 100 * 1000;
+
+ private BftsmartNodeServer nodeServer;
+ private int clientId;
+ private static final Lock authLock = new ReentrantLock();
+
+ public BftsmartConsensusManageService(BftsmartNodeServer nodeServer) {
+ this.nodeServer = nodeServer;
+ //Assume that each peer node corresponds to up to 100 gateways
+ clientId = nodeServer.getServerId() * CLIENT_RANGE;
+ }
+
+ @Override
+ public BftsmartClientIncomingSettings authClientIncoming(ClientIdentification authId) {
+ if (verify(authId)) {
+ BftsmartClientIncomingSettings clientIncomingSettings = new BftsmartClientIncomingConfig();
+
+ ((BftsmartClientIncomingConfig) clientIncomingSettings).setTopology(BinarySerializeUtils.serialize(nodeServer.getTopology()));
+ ((BftsmartClientIncomingConfig) clientIncomingSettings).setTomConfig(BinarySerializeUtils.serialize(nodeServer.getTomConfig()));
+ ((BftsmartClientIncomingConfig) clientIncomingSettings).setConsensusSettings(nodeServer.getConsensusSetting());
+ ((BftsmartClientIncomingConfig) clientIncomingSettings).setPubKey(authId.getPubKey());
+ //compute gateway id
+ try {
+ authLock.lock();
+ ((BftsmartClientIncomingConfig) clientIncomingSettings).setClientId(clientId++);
+ } finally {
+ authLock.unlock();
+ }
+
+ return clientIncomingSettings;
+
+ }
+
+ return null;
+ }
+
+ public boolean verify(ClientIdentification authId) {
+
+ SignatureFunction signatureFunction = CryptoUtils.sign(authId.getPubKey().getAlgorithm());
+
+ return signatureFunction.verify(authId.getSignature(), authId.getPubKey(), authId.getIdentityInfo());
+ }
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServer.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServer.java
new file mode 100644
index 00000000..f25c8d61
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServer.java
@@ -0,0 +1,365 @@
+package com.jd.blockchain.consensus.bftsmart.service;
+
+import java.io.ByteArrayOutputStream;
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import bftsmart.tom.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.jd.blockchain.consensus.ConsensusManageService;
+import com.jd.blockchain.consensus.NodeSettings;
+import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider;
+import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings;
+import com.jd.blockchain.consensus.bftsmart.BftsmartNodeSettings;
+import com.jd.blockchain.consensus.bftsmart.BftsmartTopology;
+import com.jd.blockchain.consensus.service.MessageHandle;
+import com.jd.blockchain.consensus.service.NodeServer;
+import com.jd.blockchain.consensus.service.ServerSettings;
+import com.jd.blockchain.consensus.service.StateHandle;
+import com.jd.blockchain.consensus.service.StateMachineReplicate;
+import com.jd.blockchain.ledger.TransactionState;
+import com.jd.blockchain.utils.PropertiesUtils;
+import com.jd.blockchain.utils.concurrent.AsyncFuture;
+import com.jd.blockchain.utils.io.BytesUtils;
+import bftsmart.reconfiguration.util.HostsConfig;
+import bftsmart.reconfiguration.util.TOMConfiguration;
+import bftsmart.tom.core.messages.TOMMessage;
+import bftsmart.tom.server.defaultservices.DefaultRecoverable;
+
+public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer {
+
+ private static Logger LOGGER = LoggerFactory.getLogger(BftsmartNodeServer.class);
+
+ private List stateHandles = new CopyOnWriteArrayList<>();
+
+ // TODO 暂不处理队列溢出问题
+ private ExecutorService notifyReplyExecutors = Executors.newSingleThreadExecutor();
+
+ private volatile Status status = Status.STOPPED;
+
+ private final Object mutex = new Object();
+
+ private volatile ServiceReplica replica;
+
+ private StateMachineReplicate stateMachineReplicate;
+
+ private ServerSettings serverSettings;
+
+ private BftsmartConsensusManageService manageService;
+
+
+ private volatile BftsmartTopology topology;
+
+ private volatile BftsmartConsensusSettings setting;
+
+ private TOMConfiguration tomConfig;
+
+ private HostsConfig hostsConfig;
+ private Properties systemConfig;
+
+ private MessageHandle messageHandle;
+
+ private String providerName;
+
+ private String realmName;
+
+ private int serverId;
+
+ public BftsmartNodeServer() {
+
+ }
+
+ public BftsmartNodeServer(ServerSettings serverSettings, MessageHandle messageHandler, StateMachineReplicate stateMachineReplicate) {
+ this.serverSettings = serverSettings;
+ this.realmName = serverSettings.getRealmName();
+ //used later
+ this.stateMachineReplicate = stateMachineReplicate;
+ this.messageHandle = messageHandler;
+ this.manageService = new BftsmartConsensusManageService(this);
+ createConfig();
+ serverId = findServerId();
+ initConfig(serverId, systemConfig, hostsConfig);
+ }
+
+ protected int findServerId() {
+ int serverId = 0;
+
+ for (int i = 0; i < hostsConfig.getNum(); i++) {
+ String host = ((BftsmartNodeSettings)serverSettings.getReplicaSettings()).getNetworkAddress().getHost();
+ int port = ((BftsmartNodeSettings)serverSettings.getReplicaSettings()).getNetworkAddress().getPort();
+
+ if (hostsConfig.getHost(i).equals(host) && hostsConfig.getPort(i) == port) {
+ serverId = i;
+ break;
+ }
+ }
+
+ return serverId;
+ }
+
+ public int getServerId() {
+ return serverId;
+ }
+
+ protected void createConfig() {
+
+ setting = ((BftsmartServerSettings) serverSettings).getConsensusSettings();
+
+ List configList = new ArrayList();
+
+ NodeSettings[] nodeSettingsArray = setting.getNodes();
+ for (NodeSettings nodeSettings : nodeSettingsArray) {
+ BftsmartNodeSettings node = (BftsmartNodeSettings)nodeSettings;
+ configList.add(new HostsConfig.Config(node.getId(), node.getNetworkAddress().getHost(), node.getNetworkAddress().getPort()));
+ }
+
+ //create HostsConfig instance based on consensus realm nodes
+ hostsConfig = new HostsConfig(configList.toArray(new HostsConfig.Config[configList.size()]));
+
+ systemConfig = PropertiesUtils.createProperties(setting.getSystemConfigs());
+
+ 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) {
+ this.tomConfig = new TOMConfiguration(id, systemsConfig, hostConfig);
+ }
+
+ @Override
+ public ConsensusManageService getManageService() {
+ return manageService;
+ }
+
+ @Override
+ public ServerSettings getSettings() {
+ return serverSettings;
+ }
+
+ @Override
+ public String getProviderName() {
+ return BftsmartConsensusProvider.NAME;
+ }
+
+ public TOMConfiguration getTomConfig() {
+ return tomConfig;
+ }
+
+ public int getId() {
+ return tomConfig.getProcessId();
+ }
+
+ public void setId(int id) {
+ if (id < 0) {
+ throw new IllegalArgumentException("ReplicaID is negative!");
+ }
+ this.tomConfig.setProcessId(id);
+
+ }
+
+ public BftsmartConsensusSettings getConsensusSetting() {
+ return setting;
+ }
+
+ public BftsmartTopology getTopology() {
+ return topology;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ @Override
+ public boolean isRunning() {
+ return status == Status.RUNNING;
+ }
+
+ public byte[] appExecuteUnordered(byte[] bytes, MessageContext messageContext) {
+ return messageHandle.processUnordered(bytes).get();
+ }
+
+ @Override
+ public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus) {
+ return appExecuteBatch(commands, msgCtxs, fromConsensus, null);
+ }
+
+ @Override
+ public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus, List replyList) {
+
+ if (replyList == null || replyList.size() == 0) {
+ throw new IllegalArgumentException();
+ }
+ // todo 此部分需要重新改造
+ /**
+ * 默认BFTSmart接口提供的commands是一个或多个共识结果的顺序集合
+ * 根据共识的规定,目前的做法是将其根据msgCtxs的内容进行分组,每组都作为一个结块标识来处理
+ * 从msgCtxs可以获取对应commands的分组情况
+ */
+ int manageConsensusId = msgCtxs[0].getConsensusId();
+ List manageConsensusCmds = new ArrayList<>();
+ List manageReplyMsgs = new ArrayList<>();
+
+ int index = 0;
+ for (MessageContext msgCtx : msgCtxs) {
+ if (msgCtx.getConsensusId() == manageConsensusId) {
+ manageConsensusCmds.add(commands[index]);
+ manageReplyMsgs.add(replyList.get(index));
+ } else {
+ // 达到结块标准,需要进行结块并应答
+ blockAndReply(manageConsensusCmds, manageReplyMsgs);
+ // 重置链表和共识ID
+ manageConsensusCmds = new ArrayList<>();
+ manageReplyMsgs = new ArrayList<>();
+ manageConsensusId = msgCtx.getConsensusId();
+ manageConsensusCmds.add(commands[index]);
+ manageReplyMsgs.add(replyList.get(index));
+ }
+ index++;
+ }
+ // 结束时,肯定有最后一个结块请求未处理
+ if (!manageConsensusCmds.isEmpty()) {
+ blockAndReply(manageConsensusCmds, manageReplyMsgs);
+ }
+ return null;
+ }
+
+ private void blockAndReply(List manageConsensusCmds, List replyList) {
+ String batchId = messageHandle.beginBatch(realmName);
+ List> asyncFutureLinkedList = new ArrayList<>(manageConsensusCmds.size());
+ try {
+ int msgId = 0;
+ for (byte[] txContent : manageConsensusCmds) {
+ AsyncFuture asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, batchId);
+ asyncFutureLinkedList.add(asyncFuture);
+ }
+ messageHandle.completeBatch(realmName, batchId);
+ messageHandle.commitBatch(realmName, batchId);
+ } catch (Exception e) {
+ // todo 需要处理应答码 404
+ messageHandle.rollbackBatch(realmName, batchId, TransactionState.CONSENSUS_ERROR.CODE);
+ }
+
+ // 通知线程单独处理应答
+ notifyReplyExecutors.execute(() -> {
+ // 应答对应的结果
+ int replyIndex = 0;
+ for(ReplyContextMessage msg : replyList) {
+ msg.setReply(asyncFutureLinkedList.get(replyIndex).get());
+ TOMMessage request = msg.getTomMessage();
+ ReplyContext replyContext = msg.getReplyContext();
+ request.reply = new TOMMessage(replyContext.getId(), request.getSession(), request.getSequence(),
+ request.getOperationId(), msg.getReply(), replyContext.getCurrentViewId(),
+ request.getReqType());
+
+ if (replyContext.getNumRepliers() > 0) {
+ bftsmart.tom.util.Logger.println("(ServiceReplica.receiveMessages) sending reply to "
+ + request.getSender() + " with sequence number " + request.getSequence()
+ + " and operation ID " + request.getOperationId() + " via ReplyManager");
+ replyContext.getRepMan().send(request);
+ } else {
+ bftsmart.tom.util.Logger.println("(ServiceReplica.receiveMessages) sending reply to "
+ + request.getSender() + " with sequence number " + request.getSequence()
+ + " and operation ID " + request.getOperationId());
+ replyContext.getReplier().manageReply(request, msg.getMessageContext());
+ }
+ replyIndex++;
+ }
+ });
+ }
+
+ //notice
+ public byte[] getSnapshot() {
+ LOGGER.debug("------- GetSnapshot...[replica.id=" + this.getId() + "]");
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ BytesUtils.writeInt(stateHandles.size(), out);
+ for (StateHandle stateHandle : stateHandles) {
+ // TODO: 测试代码;
+ return stateHandle.takeSnapshot();
+ }
+ return out.toByteArray();
+ }
+
+ public void installSnapshot(byte[] snapshot) {
+// System.out.println("Not implement!");
+ }
+
+ @Override
+ public void start() {
+ if (this.getId() < 0) {
+ throw new IllegalStateException("Unset server node ID!");
+ }
+ LOGGER.debug("=============================== Start replica ===================================");
+
+ if (status != Status.STOPPED) {
+ return;
+ }
+ synchronized (mutex) {
+ if (status != Status.STOPPED) {
+ return;
+ }
+ status = Status.STARTING;
+
+ try {
+ LOGGER.debug("Start replica...[ID=" + getId() + "]");
+ this.replica = new ServiceReplica(tomConfig, this, this);
+ this.topology = new BftsmartTopology(replica.getReplicaContext().getCurrentView());
+ status = Status.RUNNING;
+// createProxyClient();
+ LOGGER.debug(
+ "=============================== Replica started success! ===================================");
+ } catch (RuntimeException e) {
+ status = Status.STOPPED;
+ throw e;
+ }
+ }
+
+ }
+
+ @Override
+ public void stop() {
+ if (status != Status.RUNNING) {
+ return;
+ }
+ synchronized (mutex) {
+ if (status != Status.RUNNING) {
+ return;
+ }
+ status = Status.STOPPING;
+
+ try {
+ ServiceReplica rep = this.replica;
+ if (rep != null) {
+ LOGGER.debug("Stop replica...[ID=" + rep.getId() + "]");
+ this.replica = null;
+ this.topology = null;
+
+ rep.kill();
+ LOGGER.debug("Replica had stopped! --[ID=" + rep.getId() + "]");
+ }
+ } finally {
+ status = Status.STOPPED;
+ }
+ }
+ }
+
+ enum Status {
+
+ STARTING,
+
+ RUNNING,
+
+ STOPPING,
+
+ STOPPED
+
+ }
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServerFactory.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServerFactory.java
new file mode 100644
index 00000000..50ffd90f
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServerFactory.java
@@ -0,0 +1,110 @@
+package com.jd.blockchain.consensus.bftsmart.service;
+
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.consensus.NodeSettings;
+import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings;
+import com.jd.blockchain.consensus.bftsmart.BftsmartNodeSettings;
+import com.jd.blockchain.consensus.service.MessageHandle;
+import com.jd.blockchain.consensus.service.NodeServer;
+import com.jd.blockchain.consensus.service.NodeServerFactory;
+import com.jd.blockchain.consensus.service.ServerSettings;
+import com.jd.blockchain.consensus.service.StateMachineReplicate;
+import com.jd.blockchain.utils.net.NetworkAddress;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class BftsmartNodeServerFactory implements NodeServerFactory {
+
+ private static Map nodeServerMap = new ConcurrentHashMap<>();
+
+
+ @Override
+ public ServerSettings buildServerSettings(String realmName, ConsensusSettings consensusSetting, String currentNodeAddress) {
+
+ NodeSettings serverNode = null;
+
+ BftsmartServerSettingConfig serverSettings = new BftsmartServerSettingConfig();
+
+ //find current node according to current address
+ for (NodeSettings nodeSettings : consensusSetting.getNodes()) {
+ if (nodeSettings.getAddress().equals(currentNodeAddress)) {
+ serverNode = nodeSettings;
+ break;
+ }
+ }
+
+ if (serverNode == null) {
+ throw new IllegalArgumentException();
+ }
+
+ //set server settings
+ serverSettings.setRealmName(realmName);
+
+ serverSettings.setReplicaSettings(serverNode);
+
+ serverSettings.setConsensusSettings((BftsmartConsensusSettings) consensusSetting);
+
+ return serverSettings;
+
+ }
+
+ @Override
+ public NodeServer setupServer(ServerSettings serverSettings, MessageHandle messageHandler,
+ StateMachineReplicate stateMachineReplicator) {
+
+ NodeSettings[] currNodeSettings = (((BftsmartServerSettings)serverSettings).getConsensusSettings()).getNodes();
+
+ //check conflict realm
+ if (!hasIntersection(currNodeSettings)) {
+ BftsmartNodeServer nodeServer = new BftsmartNodeServer(serverSettings, messageHandler, stateMachineReplicator);
+ nodeServerMap.put(serverSettings.getRealmName(), currNodeSettings);
+ return nodeServer;
+ }
+ else {
+ throw new IllegalArgumentException("setupServer serverSettings parameters error!");
+ }
+ }
+
+
+ //check if consensus realm conflict, by this support multi ledgers
+ private boolean hasIntersection(NodeSettings[] currNodeSettings) {
+
+ int currHashCode = getHashcode(currNodeSettings);
+
+ //first check if is same consensus realm
+ for (NodeSettings[] exisitNodeSettings : nodeServerMap.values()) {
+ if (currHashCode == getHashcode(exisitNodeSettings)) {
+ return false;
+ }
+ }
+ //check conflict
+ for (NodeSettings[] exisitNodeSettings : nodeServerMap.values()) {
+ for (NodeSettings curr : currNodeSettings) {
+ for (NodeSettings exist : exisitNodeSettings) {
+ if (((BftsmartNodeSettings)curr).getNetworkAddress().equals(((BftsmartNodeSettings)exist).getNetworkAddress())) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ //compute hashcode for consensus nodes
+ private int getHashcode(NodeSettings[] nodeSettings) {
+
+ int i = 0;
+ NetworkAddress[] nodeAddrs = new NetworkAddress[nodeSettings.length];
+ for (NodeSettings setting : nodeSettings) {
+
+ nodeAddrs[i++] = ((BftsmartNodeSettings)setting).getNetworkAddress();
+ }
+ int hashCode = Arrays.hashCode(nodeAddrs);
+ return hashCode;
+
+ }
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartServerSettingConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartServerSettingConfig.java
new file mode 100644
index 00000000..16420932
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartServerSettingConfig.java
@@ -0,0 +1,43 @@
+package com.jd.blockchain.consensus.bftsmart.service;
+
+import com.jd.blockchain.consensus.NodeSettings;
+import com.jd.blockchain.consensus.bftsmart.BftsmartCommitBlockSettings;
+import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings;
+import com.jd.blockchain.consensus.bftsmart.BftsmartNodeSettings;
+import com.jd.blockchain.consensus.service.ServerSettings;
+
+public class BftsmartServerSettingConfig implements BftsmartServerSettings {
+ private NodeSettings replicaSettings;
+ private String realmName;
+ private BftsmartConsensusSettings consensusSettings;
+
+
+ @Override
+ public String getRealmName() {
+ return realmName;
+ }
+
+ public void setRealmName(String realmName) {
+ this.realmName = realmName;
+ }
+
+
+ @Override
+ public NodeSettings getReplicaSettings() {
+ return replicaSettings;
+ }
+
+ public void setReplicaSettings(NodeSettings replicaSettings) {
+ this.replicaSettings = replicaSettings;
+ }
+
+
+ @Override
+ public BftsmartConsensusSettings getConsensusSettings() {
+ return consensusSettings;
+ }
+
+ public void setConsensusSettings(BftsmartConsensusSettings consensusSettings) {
+ this.consensusSettings = consensusSettings;
+ }
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartServerSettings.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartServerSettings.java
new file mode 100644
index 00000000..1cf627d2
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartServerSettings.java
@@ -0,0 +1,11 @@
+package com.jd.blockchain.consensus.bftsmart.service;
+
+import com.jd.blockchain.consensus.bftsmart.BftsmartCommitBlockSettings;
+import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings;
+import com.jd.blockchain.consensus.service.ServerSettings;
+
+public interface BftsmartServerSettings extends ServerSettings {
+
+ BftsmartConsensusSettings getConsensusSettings();
+
+}
diff --git a/source/consensus/consensus-bftsmart/src/main/resources/bftsmart.config b/source/consensus/consensus-bftsmart/src/main/resources/bftsmart.config
new file mode 100644
index 00000000..df69caf5
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/main/resources/bftsmart.config
@@ -0,0 +1,144 @@
+# Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+############################################
+####### Communication Configurations #######
+############################################
+
+#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value)
+#This parameter is not currently being used being used
+#system.authentication.hmacAlgorithm = HmacSHA1
+
+#Specify if the communication system should use a thread to send data (true or false)
+system.communication.useSenderThread = true
+
+#Force all processes to use the same public/private keys pair and secret key. This is useful when deploying experiments
+#and benchmarks, but must not be used in production systems.
+system.communication.defaultkeys = true
+
+############################################
+### Replication Algorithm Configurations ###
+############################################
+
+#Timeout to asking for a client request
+system.totalordermulticast.timeout = 2000
+
+
+#Maximum batch size (in number of messages)
+system.totalordermulticast.maxbatchsize = 400
+
+#Number of nonces (for non-determinism actions) generated
+system.totalordermulticast.nonces = 10
+
+#if verification of leader-generated timestamps are increasing
+#it can only be used on systems in which the network clocks
+#are synchronized
+system.totalordermulticast.verifyTimestamps = false
+
+#Quantity of messages that can be stored in the receive queue of the communication system
+system.communication.inQueueSize = 500000
+
+# Quantity of messages that can be stored in the send queue of each replica
+system.communication.outQueueSize = 500000
+
+#Set to 1 if SMaRt should use signatures, set to 0 if otherwise
+system.communication.useSignatures = 0
+
+#Set to 1 if SMaRt should use MAC's, set to 0 if otherwise
+system.communication.useMACs = 1
+
+#Set to 1 if SMaRt should use the standard output to display debug messages, set to 0 if otherwise
+system.debug = 0
+
+#Print information about the replica when it is shutdown
+system.shutdownhook = true
+
+############################################
+###### State Transfer Configurations #######
+############################################
+
+#Activate the state transfer protocol ('true' to activate, 'false' to de-activate)
+system.totalordermulticast.state_transfer = true
+
+#Maximum ahead-of-time message not discarded
+system.totalordermulticast.highMark = 10000
+
+#Maximum ahead-of-time message not discarded when the replica is still on EID 0 (after which the state transfer is triggered)
+system.totalordermulticast.revival_highMark = 10
+
+#Number of ahead-of-time messages necessary to trigger the state transfer after a request timeout occurs
+system.totalordermulticast.timeout_highMark = 200
+
+############################################
+###### Log and Checkpoint Configurations ###
+############################################
+
+system.totalordermulticast.log = true
+system.totalordermulticast.log_parallel = false
+system.totalordermulticast.log_to_disk = false
+system.totalordermulticast.sync_log = false
+
+#Period at which BFT-SMaRt requests the state to the application (for the state transfer state protocol)
+system.totalordermulticast.checkpoint_period = 1000
+system.totalordermulticast.global_checkpoint_period = 120000
+
+system.totalordermulticast.checkpoint_to_disk = false
+system.totalordermulticast.sync_ckp = false
+
+
+############################################
+###### Reconfiguration Configurations ######
+############################################
+
+#The ID of the trust third party (TTP)
+system.ttp.id = 7002
+
+#This sets if the system will function in Byzantine or crash-only mode. Set to "true" to support Byzantine faults
+system.bft = true
+
+#Custom View Storage;
+#view.storage.handler=bftsmart.reconfiguration.views.DefaultViewStorage
+
+
+#Number of servers in the group
+system.servers.num = 4
+
+#Maximum number of faulty replicas
+system.servers.f = 1
+
+#Replicas ID for the initial view, separated by a comma.
+# The number of replicas in this parameter should be equal to that specified in 'system.servers.num'
+system.initial.view = 0,1,2,3
+
+#Configuration of all node servers;
+#PubKey of node server with specified ID, with base58 encoding.
+system.server.0.pubkey=
+system.server.0.network.host=127.0.0.1
+system.server.0.network.port=8900
+system.server.0.network.secure=false
+
+system.server.1.pubkey=
+system.server.1.network.host=127.0.0.1
+system.server.1.network.port=8910
+system.server.1.network.secure=false
+
+system.server.2.pubkey=
+system.server.2.network.host=127.0.0.1
+system.server.2.network.port=8920
+system.server.2.network.secure=false
+
+system.server.3.pubkey=
+system.server.3.network.host=127.0.0.1
+system.server.3.network.port=8920
+system.server.3.network.secure=false
diff --git a/source/consensus/consensus-bftsmart/src/test/java/test/com/jd/blockchain/consensus/bftsmart/proxyClientTest.java b/source/consensus/consensus-bftsmart/src/test/java/test/com/jd/blockchain/consensus/bftsmart/proxyClientTest.java
new file mode 100644
index 00000000..10462343
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/test/java/test/com/jd/blockchain/consensus/bftsmart/proxyClientTest.java
@@ -0,0 +1,136 @@
+package test.com.jd.blockchain.consensus.bftsmart;
+
+import com.jd.blockchain.consensus.NodeSettings;
+import com.jd.blockchain.consensus.bftsmart.*;
+import com.jd.blockchain.consensus.bftsmart.client.BftsmartClientConfig;
+import com.jd.blockchain.consensus.bftsmart.client.BftsmartConsensusClient;
+import com.jd.blockchain.consensus.bftsmart.client.BftsmartMessageService;
+import com.jd.blockchain.consensus.bftsmart.service.BftsmartNodeServer;
+import com.jd.blockchain.consensus.bftsmart.service.BftsmartServerSettingConfig;
+import com.jd.blockchain.consensus.service.ServerSettings;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.ledger.BlockchainKeyGenerator;
+import com.jd.blockchain.ledger.BlockchainKeyPair;
+import com.jd.blockchain.utils.PropertiesUtils;
+import com.jd.blockchain.utils.io.BytesUtils;
+import com.jd.blockchain.utils.net.NetworkAddress;
+import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils;
+import org.junit.Test;
+import org.springframework.core.io.ClassPathResource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class proxyClientTest {
+
+ int number = 1500000;
+
+ int peerStartPort = 11000;
+
+ int nodeNum = 4;
+
+ Random random = new Random();
+
+ byte[] bytes = null;
+
+ CountDownLatch startPeer = new CountDownLatch(nodeNum);
+
+ private static Properties bftsmartConf;
+
+ private final ExecutorService nodeStartPools = Executors.newCachedThreadPool();
+
+ private final ExecutorService txSendPools = Executors.newFixedThreadPool(20);
+
+ static {
+ ClassPathResource configResource = new ClassPathResource("system.config");
+ try {
+ try (InputStream in = configResource.getInputStream()) {
+ bftsmartConf = PropertiesUtils.load(in, BytesUtils.DEFAULT_CHARSET);
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ public void peerStart(BftsmartNodeServer[] nodeServers) {
+
+ BftsmartNodeSettings[] nodesSettings = new BftsmartNodeSettings[nodeNum];
+
+ for (int i = 0; i < nodeNum; i++) {
+ BlockchainKeyPair keyPair = BlockchainKeyGenerator.getInstance().generate();
+ PubKey pubKey = keyPair.getPubKey();
+ NetworkAddress peerNodeServ = new NetworkAddress("127.0.0.1", peerStartPort + i * 10);
+ NodeSettings node = new BftsmartNodeConfig(pubKey, i, peerNodeServ);
+ nodesSettings[i] = (BftsmartNodeSettings) node;
+ }
+
+ BftsmartConsensusConfig consensusConfig = new BftsmartConsensusConfig(nodesSettings, null,
+ PropertiesUtils.getOrderedValues(bftsmartConf));
+
+ for (int j = 0; j < nodeNum; j++) {
+ ServerSettings serverSettings = new BftsmartServerSettingConfig();
+ ((BftsmartServerSettingConfig) serverSettings).setReplicaSettings(nodesSettings[j]);
+ ((BftsmartServerSettingConfig) serverSettings).setConsensusSettings(consensusConfig);
+ BftsmartNodeServer server = new BftsmartNodeServer(serverSettings, null, null);
+ nodeServers[j] = server;
+ nodeStartPools.execute(() -> {
+ server.start();
+ startPeer.countDown();
+ });
+ }
+ }
+
+ public void proxyClientSend(BftsmartNodeServer nodeServer) {
+ BftsmartClientIncomingConfig clientIncomingConfig = new BftsmartClientIncomingConfig();
+ BlockchainKeyPair keyPair = BlockchainKeyGenerator.getInstance().generate();
+ clientIncomingConfig.setPubKey(keyPair.getPubKey());
+ clientIncomingConfig.setClientId(0);
+ clientIncomingConfig.setConsensusSettings(nodeServer.getConsensusSetting());
+ clientIncomingConfig.setTomConfig(BinarySerializeUtils.serialize(nodeServer.getTomConfig()));
+ clientIncomingConfig.setTopology(BinarySerializeUtils.serialize(nodeServer.getTopology()));
+
+ BftsmartClientConfig clientSettings = new BftsmartClientConfig(clientIncomingConfig);
+ BftsmartConsensusClient consensusClient = new BftsmartConsensusClient(clientSettings);
+ bytes = new byte[1024];
+
+ BftsmartMessageService messageService = (BftsmartMessageService) consensusClient.getMessageService();
+
+ for (int j = 0; j < number; j++) {
+ txSendPools.execute(() -> {
+ random.nextBytes(bytes);
+ messageService.sendOrdered(bytes);
+ });
+ }
+
+ }
+
+// @Test
+ public void sendTest() {
+
+ BftsmartNodeServer[] nodeServers = new BftsmartNodeServer[nodeNum];
+ //启动服务
+ peerStart(nodeServers);
+
+ try {
+ startPeer.await();
+ Thread.sleep(5000);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ proxyClientSend(nodeServers[0]);
+
+
+ try {
+ Thread.sleep(50000);
+ System.out.println("send test complete!");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/source/consensus/consensus-bftsmart/src/test/resources/bftsmart.config b/source/consensus/consensus-bftsmart/src/test/resources/bftsmart.config
new file mode 100644
index 00000000..970ddf4b
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/test/resources/bftsmart.config
@@ -0,0 +1,178 @@
+
+# Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+############################################
+###### Consensus Commit Block Parameters: transaction count ######
+############################################
+system.block.txsize=15
+
+############################################
+###### Consensus Commit Block Parameters: delay time ######
+############################################
+system.block.maxdelay=500
+
+############################################
+###### Consensus Participant0 ######
+############################################
+system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
+
+system.server.0.network.host=127.0.0.1
+
+system.server.0.network.port=8910
+
+system.server.0.network.secure=false
+
+############################################
+###### #Consensus Participant1 ######
+############################################
+system.server.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ
+
+system.server.1.network.host=127.0.0.1
+
+system.server.1.network.port=8920
+
+system.server.1.network.secure=false
+
+############################################
+###### #Consensus Participant2 ######
+############################################
+system.server.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R
+
+system.server.2.network.host=127.0.0.1
+
+system.server.2.network.port=8930
+
+system.server.2.network.secure=false
+
+############################################
+###### Consensus Participant3 ######
+############################################
+system.server.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR
+
+system.server.3.network.host=127.0.0.1
+
+system.server.3.network.port=8940
+
+system.server.3.network.secure=false
+
+############################################
+####### Communication Configurations #######
+############################################
+
+#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value)
+#This parameter is not currently being used being used
+#system.authentication.hmacAlgorithm = HmacSHA1
+
+#Specify if the communication system should use a thread to send data (true or false)
+system.communication.useSenderThread = true
+
+#Force all processes to use the same public/private keys pair and secret key. This is useful when deploying experiments
+#and benchmarks, but must not be used in production systems.
+system.communication.defaultkeys = true
+
+############################################
+### Replication Algorithm Configurations ###
+############################################
+
+#Number of servers in the group
+system.servers.num = 4
+
+#Maximum number of faulty replicas
+#system.servers.f = 1
+
+#Timeout to asking for a client request
+system.totalordermulticast.timeout = 2000
+
+
+#Maximum batch size (in number of messages)
+system.totalordermulticast.maxbatchsize = 400
+
+#Number of nonces (for non-determinism actions) generated
+system.totalordermulticast.nonces = 10
+
+#if verification of leader-generated timestamps are increasing
+#it can only be used on systems in which the network clocks
+#are synchronized
+system.totalordermulticast.verifyTimestamps = false
+
+#Quantity of messages that can be stored in the receive queue of the communication system
+system.communication.inQueueSize = 500000
+
+# Quantity of messages that can be stored in the send queue of each replica
+system.communication.outQueueSize = 500000
+
+#Set to 1 if SMaRt should use signatures, set to 0 if otherwise
+system.communication.useSignatures = 0
+
+#Set to 1 if SMaRt should use MAC's, set to 0 if otherwise
+system.communication.useMACs = 1
+
+#Set to 1 if SMaRt should use the standard output to display debug messages, set to 0 if otherwise
+system.debug = 0
+
+#Print information about the replica when it is shutdown
+system.shutdownhook = true
+
+############################################
+###### State Transfer Configurations #######
+############################################
+
+#Activate the state transfer protocol ('true' to activate, 'false' to de-activate)
+system.totalordermulticast.state_transfer = true
+
+#Maximum ahead-of-time message not discarded
+system.totalordermulticast.highMark = 10000
+
+#Maximum ahead-of-time message not discarded when the replica is still on EID 0 (after which the state transfer is triggered)
+system.totalordermulticast.revival_highMark = 10
+
+#Number of ahead-of-time messages necessary to trigger the state transfer after a request timeout occurs
+system.totalordermulticast.timeout_highMark = 200
+
+############################################
+###### Log and Checkpoint Configurations ###
+############################################
+
+system.totalordermulticast.log = true
+system.totalordermulticast.log_parallel = false
+system.totalordermulticast.log_to_disk = false
+system.totalordermulticast.sync_log = false
+
+#Period at which BFT-SMaRt requests the state to the application (for the state transfer state protocol)
+system.totalordermulticast.checkpoint_period = 1000
+system.totalordermulticast.global_checkpoint_period = 120000
+
+system.totalordermulticast.checkpoint_to_disk = false
+system.totalordermulticast.sync_ckp = false
+
+
+############################################
+###### Reconfiguration Configurations ######
+############################################
+
+#Replicas ID for the initial view, separated by a comma.
+# The number of replicas in this parameter should be equal to that specified in 'system.servers.num'
+#system.initial.view = 0,1,2,3
+
+#The ID of the trust third party (TTP)
+system.ttp.id = 7002
+
+#This sets if the system will function in Byzantine or crash-only mode. Set to "true" to support Byzantine faults
+system.bft = true
+
+#Custom View Storage;
+#view.storage.handler=bftsmart.reconfiguration.views.DefaultViewStorage
+
diff --git a/source/consensus/consensus-bftsmart/src/test/resources/system.config b/source/consensus/consensus-bftsmart/src/test/resources/system.config
new file mode 100644
index 00000000..275edc4e
--- /dev/null
+++ b/source/consensus/consensus-bftsmart/src/test/resources/system.config
@@ -0,0 +1,121 @@
+# Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+############################################
+####### Communication Configurations #######
+############################################
+
+#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value)
+#This parameter is not currently being used being used
+#system.authentication.hmacAlgorithm = HmacSHA1
+
+#Specify if the communication system should use a thread to send data (true or false)
+system.communication.useSenderThread = true
+
+#Force all processes to use the same public/private keys pair and secret key. This is useful when deploying experiments
+#and benchmarks, but must not be used in production systems.
+system.communication.defaultkeys = true
+
+############################################
+### Replication Algorithm Configurations ###
+############################################
+
+#Number of servers in the group
+system.servers.num = 4
+
+#Maximum number of faulty replicas
+#system.servers.f = 1
+
+#Timeout to asking for a client request
+system.totalordermulticast.timeout = 2000
+
+
+#Maximum batch size (in number of messages)
+system.totalordermulticast.maxbatchsize = 400
+
+#Number of nonces (for non-determinism actions) generated
+system.totalordermulticast.nonces = 10
+
+#if verification of leader-generated timestamps are increasing
+#it can only be used on systems in which the network clocks
+#are synchronized
+system.totalordermulticast.verifyTimestamps = false
+
+#Quantity of messages that can be stored in the receive queue of the communication system
+system.communication.inQueueSize = 500000
+
+# Quantity of messages that can be stored in the send queue of each replica
+system.communication.outQueueSize = 500000
+
+#Set to 1 if SMaRt should use signatures, set to 0 if otherwise
+system.communication.useSignatures = 0
+
+#Set to 1 if SMaRt should use MAC's, set to 0 if otherwise
+system.communication.useMACs = 1
+
+#Set to 1 if SMaRt should use the standard output to display debug messages, set to 0 if otherwise
+system.debug = 0
+
+#Print information about the replica when it is shutdown
+system.shutdownhook = true
+
+############################################
+###### State Transfer Configurations #######
+############################################
+
+#Activate the state transfer protocol ('true' to activate, 'false' to de-activate)
+system.totalordermulticast.state_transfer = true
+
+#Maximum ahead-of-time message not discarded
+system.totalordermulticast.highMark = 10000
+
+#Maximum ahead-of-time message not discarded when the replica is still on EID 0 (after which the state transfer is triggered)
+system.totalordermulticast.revival_highMark = 10
+
+#Number of ahead-of-time messages necessary to trigger the state transfer after a request timeout occurs
+system.totalordermulticast.timeout_highMark = 200
+
+############################################
+###### Log and Checkpoint Configurations ###
+############################################
+
+system.totalordermulticast.log = true
+system.totalordermulticast.log_parallel = false
+system.totalordermulticast.log_to_disk = false
+system.totalordermulticast.sync_log = false
+
+#Period at which BFT-SMaRt requests the state to the application (for the state transfer state protocol)
+system.totalordermulticast.checkpoint_period = 1000
+system.totalordermulticast.global_checkpoint_period = 120000
+
+system.totalordermulticast.checkpoint_to_disk = false
+system.totalordermulticast.sync_ckp = false
+
+
+############################################
+###### Reconfiguration Configurations ######
+############################################
+
+#Replicas ID for the initial view, separated by a comma.
+# The number of replicas in this parameter should be equal to that specified in 'system.servers.num'
+#system.initial.view = 0,1,2,3
+
+#The ID of the trust third party (TTP)
+system.ttp.id = 7002
+
+#This sets if the system will function in Byzantine or crash-only mode. Set to "true" to support Byzantine faults
+system.bft = true
+
+#Custom View Storage;
+#view.storage.handler=bftsmart.reconfiguration.views.DefaultViewStorage
diff --git a/source/consensus/consensus-framework/pom.xml b/source/consensus/consensus-framework/pom.xml
new file mode 100644
index 00000000..53f87df7
--- /dev/null
+++ b/source/consensus/consensus-framework/pom.xml
@@ -0,0 +1,29 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ consensus
+ 0.8.2.RELEASE
+
+ consensus-framework
+
+
+
+ com.jd.blockchain
+ binary-proto
+ ${project.version}
+
+
+ com.jd.blockchain
+ crypto-framework
+ ${project.version}
+
+
+ com.jd.blockchain
+ utils-common
+ ${project.version}
+
+
+
\ No newline at end of file
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ActionMessage.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ActionMessage.java
new file mode 100644
index 00000000..6b99fe9e
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ActionMessage.java
@@ -0,0 +1,25 @@
+package com.jd.blockchain.consensus;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author huanghaiquan
+ *
+ */
+@Target({ ElementType.PARAMETER })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ActionMessage {
+
+ /**
+ * 请求参数转换器;
+ *
+ * 指定一个 {@link BinaryMessageConverter} 接口的实现类;
+ *
+ * @return
+ */
+ Class> converter();
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncActionResponse.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncActionResponse.java
new file mode 100644
index 00000000..992e448a
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncActionResponse.java
@@ -0,0 +1,7 @@
+package com.jd.blockchain.consensus;
+
+public interface AsyncActionResponse {
+
+ byte[] process();
+
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncInvoker.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncInvoker.java
new file mode 100644
index 00000000..72969da7
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncInvoker.java
@@ -0,0 +1,59 @@
+package com.jd.blockchain.consensus;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import com.jd.blockchain.utils.concurrent.AsyncFuture;
+
+public class AsyncInvoker {
+
+ private static ThreadLocal> resultHolder;
+
+ static {
+ resultHolder = new ThreadLocal<>();
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public static T asynchorize(Class serviceClazz, T serviceInstance) {
+ if (serviceInstance instanceof AsyncService) {
+ return (T) Proxy.newProxyInstance(serviceClazz.getClassLoader(), new Class>[] { serviceClazz },
+ new AsyncInvocationHandle(serviceClazz, (AsyncService) serviceInstance));
+ }
+ throw new IllegalArgumentException("The specified service instance is not supported by this asynchronize util!");
+ }
+
+ @SuppressWarnings("unchecked")
+ public static AsyncFuture call(T methodCall){
+ AsyncFuture result = (AsyncFuture) resultHolder.get();
+ resultHolder.set(null);
+ return result;
+ }
+
+
+
+ private static class AsyncInvocationHandle implements InvocationHandler {
+
+ private Class serviceClazz;
+ private AsyncService serviceInstance;
+
+ public AsyncInvocationHandle(Class serviceClazz, AsyncService serviceInstance) {
+ this.serviceInstance = serviceInstance;
+ this.serviceClazz = serviceClazz;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ resultHolder.remove();
+ if (method.getDeclaringClass() == serviceClazz) {
+ //async invoke;
+ AsyncFuture asyncResult = serviceInstance.invoke(method, args);
+ resultHolder.set(asyncResult);
+ return null;
+ }
+ return method.invoke(serviceInstance, args);
+ }
+
+ }
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncService.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncService.java
new file mode 100644
index 00000000..2b113718
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncService.java
@@ -0,0 +1,11 @@
+package com.jd.blockchain.consensus;
+
+import java.lang.reflect.Method;
+
+import com.jd.blockchain.utils.concurrent.AsyncFuture;
+
+public interface AsyncService {
+
+ AsyncFuture invoke(Method method, Object[] args);
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/BinaryMessageConverter.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/BinaryMessageConverter.java
new file mode 100644
index 00000000..5fe2f44e
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/BinaryMessageConverter.java
@@ -0,0 +1,9 @@
+package com.jd.blockchain.consensus;
+
+public interface BinaryMessageConverter {
+
+ byte[] encode(Object message);
+
+ Object decode(byte[] messageBytes);
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentification.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentification.java
new file mode 100644
index 00000000..73198cb5
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentification.java
@@ -0,0 +1,50 @@
+package com.jd.blockchain.consensus;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.asymmetric.SignatureDigest;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * 客户端的身份证明;
+ *
+ * @author huanghaiquan
+ *
+ */
+@DataContract(code = TypeCodes.CLIENT_IDENTIFICATION)
+public interface ClientIdentification {
+
+ /**
+ * 身份信息;
+ *
+ * @return
+ */
+ @DataField(order = 0, primitiveType = ValueType.BYTES)
+ byte[] getIdentityInfo();
+
+ /**
+ * 客户端的公钥;
+ *
+ * @return
+ */
+ @DataField(order = 1, primitiveType = ValueType.BYTES)
+ PubKey getPubKey();
+
+ /**
+ * 客户端对认证信息的签名;
+ *
+ * @return
+ */
+ @DataField(order = 2, primitiveType = ValueType.BYTES)
+ SignatureDigest getSignature();
+
+ /**
+ * 具体实现类
+ *
+ * @return
+ */
+ @DataField(order = 3, primitiveType = ValueType.TEXT)
+ String getProviderName();
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentifications.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentifications.java
new file mode 100644
index 00000000..0b620074
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentifications.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.ClientIdentifications
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/19 下午3:58
+ * Description:
+ */
+package com.jd.blockchain.consensus;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/19
+ * @since 1.0.0
+ */
+@DataContract(code = TypeCodes.CLIENT_IDENTIFICATIONS)
+public interface ClientIdentifications {
+
+ @DataField(order = 0, list = true, refContract = true, genericContract = true)
+ ClientIdentification[] getClientIdentifications();
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentificationsProvider.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentificationsProvider.java
new file mode 100644
index 00000000..e2e83453
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentificationsProvider.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.ClientIdentificationsProvider
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/19 下午3:59
+ * Description:
+ */
+package com.jd.blockchain.consensus;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/19
+ * @since 1.0.0
+ */
+
+public class ClientIdentificationsProvider implements ClientIdentifications {
+
+ private List clientIdentifications = new ArrayList<>();
+
+ public void add(ClientIdentification clientIdentification) {
+ clientIdentifications.add(clientIdentification);
+ }
+
+ @Override
+ public ClientIdentification[] getClientIdentifications() {
+ return clientIdentifications.toArray(new ClientIdentification[clientIdentifications.size()]);
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIncomingSettings.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIncomingSettings.java
new file mode 100644
index 00000000..d0644443
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIncomingSettings.java
@@ -0,0 +1,41 @@
+package com.jd.blockchain.consensus;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * 共识网络的客户接入参数;
+ *
+ * @author huanghaiquan
+ *
+ */
+@DataContract(code = TypeCodes.CONSENSUS_CLI_INCOMING_SETTINGS)
+public interface ClientIncomingSettings {
+
+ /**
+ * 分配的客户端ID;
+ *
+ * @return
+ */
+ @DataField(order = 0, primitiveType = ValueType.INT32)
+ int getClientId();
+
+ /**
+ * ProviderName
+ *
+ * @return
+ */
+ @DataField(order = 1, primitiveType = ValueType.TEXT)
+ String getProviderName();
+
+ /**
+ * 共识网络的配置参数;
+ *
+ * @return
+ */
+ @DataField(order = 2, refContract = true, genericContract = true)
+ ConsensusSettings getConsensusSettings();
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusManageService.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusManageService.java
new file mode 100644
index 00000000..c589fc6b
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusManageService.java
@@ -0,0 +1,20 @@
+package com.jd.blockchain.consensus;
+
+/**
+ * 共识节点的管理服务;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface ConsensusManageService {
+
+ /**
+ * 对客户端的接入进行认证;
+ *
+ * @param authId
+ * 客户端的身份信息;
+ * @return 如果通过认证,则返回接入参数;如果认证失败,则返回 null;
+ */
+ ClientIncomingSettings authClientIncoming(ClientIdentification authId) throws ConsensusSecurityException;
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusProvider.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusProvider.java
new file mode 100644
index 00000000..f3c41ef9
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusProvider.java
@@ -0,0 +1,12 @@
+package com.jd.blockchain.consensus;
+
+import com.jd.blockchain.consensus.client.ConsensusClientProvider;
+import com.jd.blockchain.consensus.service.ConsensusServiceProvider;
+
+public interface ConsensusProvider extends ConsensusClientProvider, ConsensusServiceProvider {
+
+ String getName();
+
+ SettingsFactory getSettingsFactory();
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusProviders.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusProviders.java
new file mode 100644
index 00000000..c1b8c58c
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusProviders.java
@@ -0,0 +1,43 @@
+package com.jd.blockchain.consensus;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.util.ClassUtils;
+
+public class ConsensusProviders {
+
+ private static final Object mutex = new Object();
+
+ private static Map providers = new ConcurrentHashMap<>();
+
+ public static ConsensusProvider getProvider(String className) {
+ ConsensusProvider provider = providers.get(className);
+ if (provider == null) {
+ synchronized (mutex) {
+ provider = providers.get(className);
+ if (provider == null) {
+ provider = loadProvider(ConsensusProvider.class, className);
+ providers.put(className, provider);
+ }
+ }
+ }
+ return provider;
+ }
+
+ private static T loadProvider(Class assignableTo, String implementClassName) {
+ Class> providerClass = ClassUtils.resolveClassName(implementClassName,
+ ConsensusProviders.class.getClassLoader());
+ if (!assignableTo.isAssignableFrom(providerClass)) {
+ throw new IllegalArgumentException(
+ String.format("%s is not implement %s!", implementClassName, assignableTo.getName()));
+ }
+ return BeanUtils.instantiateClass(providerClass, assignableTo);
+ }
+
+ public static void registerProvider(ConsensusProvider provider) {
+ providers.put(provider.getName(), provider);
+ }
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSecurityException.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSecurityException.java
new file mode 100644
index 00000000..36968fd6
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSecurityException.java
@@ -0,0 +1,18 @@
+package com.jd.blockchain.consensus;
+
+public class ConsensusSecurityException extends Exception{
+
+ private static final long serialVersionUID = -164820276123627155L;
+
+ public ConsensusSecurityException() {
+ }
+
+ public ConsensusSecurityException(String message) {
+ super(message);
+ }
+
+ public ConsensusSecurityException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusService.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusService.java
new file mode 100644
index 00000000..f7cf1cf8
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusService.java
@@ -0,0 +1,12 @@
+package com.jd.blockchain.consensus;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ConsensusService {
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSettings.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSettings.java
new file mode 100644
index 00000000..a2d99ad3
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSettings.java
@@ -0,0 +1,24 @@
+package com.jd.blockchain.consensus;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+
+/**
+ * 共识网络的配置参数;
+ *
+ * @author huanghaiquan
+ *
+ */
+@DataContract(code = TypeCodes.CONSENSUS_SETTINGS)
+public interface ConsensusSettings {
+
+ /**
+ * 共识网络中的节点列表;
+ *
+ * @return
+ */
+ @DataField(order = 0, refContract = true, list = true, genericContract = true)
+ NodeSettings[] getNodes();
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSettingsBuilder.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSettingsBuilder.java
new file mode 100644
index 00000000..8adc61dd
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSettingsBuilder.java
@@ -0,0 +1,23 @@
+package com.jd.blockchain.consensus;
+
+import java.util.Properties;
+
+public interface ConsensusSettingsBuilder {
+
+ /**
+ * 从属性表中解析生成共识网络的参数配置;
+ *
+ * @param props
+ * 属性表;
+ * @param keyPrefix
+ * 属性的key 的前缀;
+ * 在解析过程中,以具体协议实现的标准参数的key 加入此前缀后从属性表中检索参数值;
+ * 如果指定为 null 或者空白,则忽略此参数;
+ * @return
+ */
+ ConsensusSettings createSettings(Properties props);
+
+ Properties createPropertiesTemplate();
+
+ void writeSettings(ConsensusSettings settings, Properties props);
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/GroupIndexer.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/GroupIndexer.java
new file mode 100644
index 00000000..11330d16
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/GroupIndexer.java
@@ -0,0 +1,11 @@
+package com.jd.blockchain.consensus;
+
+/**
+ * @author huanghaiquan
+ *
+ */
+public interface GroupIndexer {
+
+ byte[] getGroupId(Object[] messageObjects);
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/MessageService.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/MessageService.java
new file mode 100644
index 00000000..4740e6d9
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/MessageService.java
@@ -0,0 +1,11 @@
+package com.jd.blockchain.consensus;
+
+import com.jd.blockchain.utils.concurrent.AsyncFuture;
+
+public interface MessageService {
+
+ AsyncFuture sendOrdered(byte[] message);
+
+ AsyncFuture sendUnordered(byte[] message);
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/NodeSettings.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/NodeSettings.java
new file mode 100644
index 00000000..31ae9a36
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/NodeSettings.java
@@ -0,0 +1,35 @@
+package com.jd.blockchain.consensus;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ * 节点的配置参数;
+ *
+ * @author huanghaiquan
+ *
+ */
+@DataContract(code=TypeCodes.CONSENSUS_NODE_SETTINGS)
+public interface NodeSettings {
+
+ /**
+ * 用于标识一个节点的地址;
+ *
+ * 该值没有一个通用定义,可以是具体的通讯地址,也可以只是标识符,或者区块链地址,而是由特定的共识服务提供者的实现进行定义;
+ *
+ * @return
+ */
+ @DataField(order=0, primitiveType=ValueType.TEXT)
+ String getAddress();
+
+ /**
+ * Base58 格式的公钥;
+ *
+ * @return
+ */
+ @DataField(order = 1, primitiveType = ValueType.BYTES)
+ PubKey getPubKey();
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/OrderedAction.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/OrderedAction.java
new file mode 100644
index 00000000..f9415d64
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/OrderedAction.java
@@ -0,0 +1,35 @@
+package com.jd.blockchain.consensus;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 标识一个共识方法调用模式为“有序的消息调用”;
+ *
+ * @author huanghaiquan
+ *
+ */
+@Target({ ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface OrderedAction {
+
+ /**
+ * 请求分组的索引器;
+ *
+ * 指定一个 {@link GroupIndexer} 接口的实现类,用于根据请求消息列表来生成共识的分组ID;
+ * @return
+ */
+ Class> groupIndexer() ;
+
+ /**
+ * 回复消息转换器;
+ *
+ * 指定一个 {@link BinaryMessageConverter} 接口的实现类;
+ *
+ * @return
+ */
+ Class> responseConverter();
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/SettingsFactory.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/SettingsFactory.java
new file mode 100644
index 00000000..449e39fa
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/SettingsFactory.java
@@ -0,0 +1,21 @@
+package com.jd.blockchain.consensus;
+
+import com.jd.blockchain.utils.io.BytesEncoder;
+
+/**
+ * 配置参数的编码器;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface SettingsFactory {
+
+ ConsensusSettingsBuilder getConsensusSettingsBuilder();
+
+ BytesEncoder getConsensusSettingsEncoder();
+
+ BytesEncoder getIncomingSettingsEncoder();
+
+
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/Topology.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/Topology.java
new file mode 100644
index 00000000..dbb2fd3b
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/Topology.java
@@ -0,0 +1,9 @@
+package com.jd.blockchain.consensus;
+
+import java.io.Serializable;
+
+public interface Topology extends Serializable {
+ int getId();
+
+ Topology copyOf();
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/UnorderedAction.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/UnorderedAction.java
new file mode 100644
index 00000000..aa62b9fe
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/UnorderedAction.java
@@ -0,0 +1,22 @@
+package com.jd.blockchain.consensus;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author huanghaiquan
+ *
+ */
+@Target({ ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UnorderedAction {
+ /**
+ * 请求分组的索引器;
+ *
+ * 指定一个 {@link GroupIndexer} 接口的实现类,用于根据请求消息列表来生成共识的分组ID;
+ * @return
+ */
+ Class> groupIndexer() ;
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionRequest.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionRequest.java
new file mode 100644
index 00000000..b88fc0f5
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionRequest.java
@@ -0,0 +1,30 @@
+package com.jd.blockchain.consensus.action;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.ValueType;
+
+@DataContract(code= TypeCodes.CONSENSUS_ACTION_REQUEST)
+public interface ActionRequest {
+
+ @DataField(order=1, list=true, primitiveType= ValueType.INT8)
+ byte[] getGroupId();
+
+ @DataField(order=2, primitiveType=ValueType.TEXT)
+ String getHandleType();
+
+ @DataField(order=3, primitiveType=ValueType.TEXT)
+ String getHandleMethod();
+
+// String getMessageType();
+
+ @DataField(order=4, list=true, primitiveType= ValueType.INT8)
+ byte[] getMessageBody();
+
+ @DataField(order=5, primitiveType= ValueType.TEXT)
+ String getTransactionType();
+
+// String getReponseType();
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionRequestData.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionRequestData.java
new file mode 100644
index 00000000..aa1514bb
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionRequestData.java
@@ -0,0 +1,60 @@
+//package com.jd.blockchain.consensus.action;
+//
+//public class ActionRequestData implements ActionRequest{
+//
+// private byte[] groupId;
+//
+// private String handleType;
+//
+// private String handleMethod;
+//
+// private byte[] messageBody;
+//
+// private String transactionType;
+//
+// @Override
+// public byte[] getGroupId() {
+// return groupId;
+// }
+//
+// @Override
+// public String getHandleType() {
+// return handleType;
+// }
+//
+// @Override
+// public String getHandleMethod() {
+// return handleMethod;
+// }
+//
+// @Override
+// public byte[] getMessageBody() {
+// return messageBody;
+// }
+//
+// @Override
+// public String getTransactionType() {
+// return transactionType;
+// }
+//
+// public void setGroupId(byte[] groupId) {
+// this.groupId = groupId;
+// }
+//
+// public void setHandleType(String handleType) {
+// this.handleType = handleType;
+// }
+//
+// public void setHandleMethod(String handleMethod) {
+// this.handleMethod = handleMethod;
+// }
+//
+// public void setMessageBody(byte[] messageBody) {
+// this.messageBody = messageBody;
+// }
+//
+// public void setTransactionType(String transactionType) {
+// this.transactionType = transactionType;
+// }
+//
+//}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionResponse.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionResponse.java
new file mode 100644
index 00000000..8e388aa2
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionResponse.java
@@ -0,0 +1,23 @@
+package com.jd.blockchain.consensus.action;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.ValueType;
+
+@DataContract(code= TypeCodes.CONSENSUS_ACTION_RESPONSE)
+public interface ActionResponse {
+
+ @DataField(order=1, list=true, primitiveType= ValueType.INT8)
+ byte[] getMessage();
+
+ @DataField(order=2, primitiveType=ValueType.BOOLEAN)
+ boolean getError();
+
+ @DataField(order=3, primitiveType=ValueType.TEXT)
+ String getErrorMessage();
+
+ @DataField(order=4, primitiveType=ValueType.TEXT)
+ String getErrorType();
+
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionResponseData.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionResponseData.java
new file mode 100644
index 00000000..3a8e3ffb
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionResponseData.java
@@ -0,0 +1,49 @@
+package com.jd.blockchain.consensus.action;
+
+public class ActionResponseData implements ActionResponse {
+
+ private byte[] message;
+
+ private boolean error = false;
+
+ private String errorMessage;
+
+ private String errorType;
+
+ @Override
+ public byte[] getMessage() {
+ return message;
+ }
+
+ public void setMessage(byte[] message) {
+ this.message = message;
+ }
+
+ @Override
+ public boolean getError() {
+ return error;
+ }
+
+ public void setError(boolean error) {
+ this.error = error;
+ }
+
+ @Override
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ @Override
+ public String getErrorType() {
+ return errorType;
+ }
+
+ public void setErrorType(String errorType) {
+ this.errorType = errorType;
+ }
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ClientFactory.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ClientFactory.java
new file mode 100644
index 00000000..0f35c76e
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ClientFactory.java
@@ -0,0 +1,50 @@
+package com.jd.blockchain.consensus.client;
+
+import com.jd.blockchain.consensus.ClientIdentification;
+import com.jd.blockchain.consensus.ClientIncomingSettings;
+import com.jd.blockchain.consensus.ConsensusManageService;
+import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair;
+
+public interface ClientFactory {
+
+ /**
+ * 创建客户端的认证身份;
+ *
+ * @param clientKeyPair
+ * @return
+ */
+ ClientIdentification buildAuthId(CryptoKeyPair clientKeyPair);
+
+ /**
+ * 根据接入配置信息创建客户端的本地连接配置;
+ *
+ * @param incomingSettings
+ * @return
+ */
+ ClientSettings buildClientSettings(ClientIncomingSettings incomingSettings);
+
+ /**
+ * 创建共识管理服务的客户端代理;
+ *
+ * 当一个共识网络的节点启动共识服务之后,其中的一些或者全部节点会暴露一个专门的管理服务端口,用于提供客户端接入认证服务和新节点接入认证服务;
+ *
+ *
+ *
+ * 作为客户端,可以选择其中的一个或者多个节点的管理服务端口进行接入认证;
+ *
+ * 不同的共识算法可以自行决定实现不同的连接策略,例如:从参数指定的地址列表中随机挑选一个可成功建立连接的节点,或者同时连接多个以进行客户端交叉验证;
+ *
+ * @param serviceNodes
+ * @return
+ */
+ ConsensusManageService createManageServiceClient(String[] serviceNodes);
+
+ /**
+ * 根据共识客户端的配置信息建立一个共识客户端实例;
+ *
+ * @param settings
+ * @return
+ */
+ ConsensusClient setupClient(ClientSettings settings);
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ClientSettings.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ClientSettings.java
new file mode 100644
index 00000000..5551e18a
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ClientSettings.java
@@ -0,0 +1,35 @@
+package com.jd.blockchain.consensus.client;
+
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+
+/**
+ * 共识客户端的配置参数;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface ClientSettings {
+
+ /**
+ * 客户端ID;
+ *
+ * @return
+ */
+ int getClientId();
+
+ /**
+ * 客户端的公钥;
+ *
+ * @return
+ */
+ PubKey getClientPubKey();
+
+ /**
+ * 共识网络的配置参数;
+ *
+ * @return
+ */
+ ConsensusSettings getConsensusSettings();
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ConsensusClient.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ConsensusClient.java
new file mode 100644
index 00000000..7e41f1d4
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ConsensusClient.java
@@ -0,0 +1,41 @@
+package com.jd.blockchain.consensus.client;
+
+import java.io.Closeable;
+
+import com.jd.blockchain.consensus.MessageService;
+
+public interface ConsensusClient extends Closeable {
+
+ /**
+ * 消息服务;
+ *
+ * @return
+ */
+ MessageService getMessageService();
+
+ /**
+ * 共识客户端的配置信息;
+ *
+ * @return
+ */
+ ClientSettings getSettings();
+
+ /**
+ * 是否已连接;
+ *
+ * @return
+ */
+ boolean isConnected();
+
+ /**
+ * 接入共识网络;
+ */
+ void connect();
+
+ /**
+ * 断开与共识网络的连接;
+ */
+ @Override
+ void close();
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ConsensusClientProvider.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ConsensusClientProvider.java
new file mode 100644
index 00000000..fe1c2c54
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ConsensusClientProvider.java
@@ -0,0 +1,9 @@
+package com.jd.blockchain.consensus.client;
+
+import com.jd.blockchain.consensus.SettingsFactory;
+
+public interface ConsensusClientProvider {
+
+
+ ClientFactory getClientFactory();
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/event/EventEntity.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/event/EventEntity.java
new file mode 100644
index 00000000..e93ccd9b
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/event/EventEntity.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: EventEntity
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午11:18
+ * Description:
+ */
+package com.jd.blockchain.consensus.event;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class EventEntity {
+
+ private T entity;
+
+ public T getEntity() {
+ return this.entity;
+ }
+
+ public void setEntity(T entity) {
+ this.entity = entity;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/event/EventProducer.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/event/EventProducer.java
new file mode 100644
index 00000000..baa5e0a3
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/event/EventProducer.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.sdk.exchange.EventProducer
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午11:15
+ * Description:
+ */
+package com.jd.blockchain.consensus.event;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public interface EventProducer {
+
+ public void publish(T t);
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/ConsensusServiceProvider.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/ConsensusServiceProvider.java
new file mode 100644
index 00000000..8feea616
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/ConsensusServiceProvider.java
@@ -0,0 +1,7 @@
+package com.jd.blockchain.consensus.service;
+
+public interface ConsensusServiceProvider {
+
+ NodeServerFactory getServerFactory();
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/MessageHandle.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/MessageHandle.java
new file mode 100644
index 00000000..4e0e1b89
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/MessageHandle.java
@@ -0,0 +1,62 @@
+package com.jd.blockchain.consensus.service;
+
+import com.jd.blockchain.utils.concurrent.AsyncFuture;
+
+/**
+ * 消息处理器;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface MessageHandle {
+
+ /**
+ * 开始一个新批次来处理有序的消息;
+ *
+ * @return 返回新批次的 ID ;
+ */
+ String beginBatch(String realmName);
+
+ /**
+ * 处理有序的消息;
+ *
+ * @param messageId
+ * 消息ID;
+ * @param message
+ * 消息内容;
+ * @param batchId
+ * 批次ID;
+ */
+ AsyncFuture processOrdered(int messageId, byte[] message, String realmName, String batchId);
+
+ /**
+ * 完成处理批次,返回要进行一致性校验的状态快照;
+ *
+ * @param batchId
+ * @return
+ */
+ StateSnapshot completeBatch(String realmName, String batchId);
+
+ /**
+ * 提交处理批次;
+ *
+ * @param batchId
+ */
+ void commitBatch(String realmName, String batchId);
+
+ /**
+ * 回滚处理批次;
+ *
+ * @param batchId
+ */
+ void rollbackBatch(String realmName, String batchId, int reasonCode);
+
+ /**
+ * 处理无序消息;
+ *
+ * @param message
+ * @return
+ */
+ AsyncFuture processUnordered(byte[] message);
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/NodeServer.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/NodeServer.java
new file mode 100644
index 00000000..8fb50f2d
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/NodeServer.java
@@ -0,0 +1,19 @@
+package com.jd.blockchain.consensus.service;
+
+import com.jd.blockchain.consensus.ConsensusManageService;
+
+public interface NodeServer {
+
+ String getProviderName();
+
+ ConsensusManageService getManageService();
+
+ ServerSettings getSettings();
+
+ boolean isRunning();
+
+ void start();
+
+ void stop();
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/NodeServerFactory.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/NodeServerFactory.java
new file mode 100644
index 00000000..67e60d22
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/NodeServerFactory.java
@@ -0,0 +1,38 @@
+package com.jd.blockchain.consensus.service;
+
+import com.jd.blockchain.consensus.ConsensusSettings;
+
+/**
+ * 共识节点服务器的工厂;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface NodeServerFactory {
+
+ /**
+ * 构建一个共识节点的参数配置;
+ *
+ * @param realmName
+ * 共识域的名称;
+ * @param consensusSetting
+ * 共识配置;
+ * @param currentNodeAddress
+ * 共识节点的虚拟地址;必须是 {@link ConsensusSettings#getNodes()} 中的一项;
+ * @return 共识节点的参数配置;
+ */
+ ServerSettings buildServerSettings(String realmName, ConsensusSettings consensusSetting, String currentNodeAddress);
+
+ /**
+ * 创建一个节点服务器;
+ *
+ * @param serverSettings
+ * 服务器配置;
+ * @param messageHandler
+ * @param stateMachineReplicator
+ * @return
+ */
+ NodeServer setupServer(ServerSettings serverSettings, MessageHandle messageHandler,
+ StateMachineReplicate stateMachineReplicator);
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/ServerSettings.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/ServerSettings.java
new file mode 100644
index 00000000..14454a72
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/ServerSettings.java
@@ -0,0 +1,17 @@
+package com.jd.blockchain.consensus.service;
+
+import com.jd.blockchain.consensus.NodeSettings;
+
+/**
+ * Replica 服务器的本地配置;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface ServerSettings {
+
+ String getRealmName();
+
+ NodeSettings getReplicaSettings();
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateHandle.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateHandle.java
new file mode 100644
index 00000000..b0aca23f
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateHandle.java
@@ -0,0 +1,9 @@
+package com.jd.blockchain.consensus.service;
+
+public interface StateHandle {
+
+ byte[] takeSnapshot();
+
+ void installSnapshot(byte[] snapshot);
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateMachineReplicate.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateMachineReplicate.java
new file mode 100644
index 00000000..a9929c0b
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateMachineReplicate.java
@@ -0,0 +1,58 @@
+package com.jd.blockchain.consensus.service;
+
+import java.io.InputStream;
+import java.util.Iterator;
+
+/**
+ * 状态机复制接口;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface StateMachineReplicate {
+
+ /**
+ * 获取最新的状态编号;
+ *
+ * 注:新的状态编号总数比旧的状态编号大;
+ *
+ * @return
+ */
+ long getLatestStateID(String realmName);
+
+ /**
+ * 返回指定状态编号的快照;
+ *
+ * @param stateId
+ * @return
+ */
+ StateSnapshot getSnapshot(String realmName, long stateId);
+
+ /**
+ * 返回包含指定的起止状态编号在内的全部状态快照;
+ *
+ * @param fromStateId
+ * 起始的状态编号(含);
+ * @param toStateId
+ * 截止的状态编号(含);
+ * @return
+ */
+ Iterator getSnapshots(String realmName, long fromStateId, long toStateId);
+
+ /**
+ * 读状态数据;
+ *
+ * @param stateId
+ * @return
+ */
+ InputStream readState(String realmName, long stateId);
+
+ /**
+ * 装载状态数据;
+ *
+ * @param snapshot
+ * @param state
+ */
+ void setupState(String realmName, StateSnapshot snapshot, InputStream state);
+
+}
diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateSnapshot.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateSnapshot.java
new file mode 100644
index 00000000..883e4e03
--- /dev/null
+++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateSnapshot.java
@@ -0,0 +1,24 @@
+package com.jd.blockchain.consensus.service;
+
+/**
+ * 状态快照;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface StateSnapshot {
+
+ /**
+ * 状态的唯一编号;
+ *
+ * @return
+ */
+ long getId();
+
+ /**
+ * 状态的快照数据;
+ *
+ * @return
+ */
+ byte[] getSnapshot();
+}
diff --git a/source/consensus/consensus-mq/pom.xml b/source/consensus/consensus-mq/pom.xml
new file mode 100644
index 00000000..7e5a6a0b
--- /dev/null
+++ b/source/consensus/consensus-mq/pom.xml
@@ -0,0 +1,71 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ consensus
+ 0.8.2.RELEASE
+
+ consensus-mq
+
+ consensus-mq
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+
+ com.jd.blockchain
+ consensus-framework
+ ${project.version}
+
+
+ com.jd.blockchain
+ tools-keygen
+ ${project.version}
+
+
+ com.jd.blockchain
+ ledger-model
+ ${project.version}
+
+
+
+ com.lmax
+ disruptor
+
+
+ io.nats
+ jnats
+
+
+
+ com.rabbitmq
+ amqp-client
+
+
+
+ junit
+ junit
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
+
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/MsgQueueConsensusProvider.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/MsgQueueConsensusProvider.java
new file mode 100644
index 00000000..4bd555ce
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/MsgQueueConsensusProvider.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/18 下午2:50
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq;
+
+import com.jd.blockchain.consensus.ConsensusProvider;
+import com.jd.blockchain.consensus.SettingsFactory;
+import com.jd.blockchain.consensus.client.ClientFactory;
+import com.jd.blockchain.consensus.mq.client.MsgQueueClientFactory;
+import com.jd.blockchain.consensus.mq.config.MsgQueueSettingsFactory;
+import com.jd.blockchain.consensus.mq.server.MsgQueueNodeServerFactory;
+import com.jd.blockchain.consensus.service.NodeServerFactory;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/18
+ * @since 1.0.0
+ */
+
+public class MsgQueueConsensusProvider implements ConsensusProvider {
+
+ public static final String NAME = MsgQueueConsensusProvider.class.getName();
+
+ private static MsgQueueSettingsFactory settingsFactory = new MsgQueueSettingsFactory();
+
+ private static MsgQueueClientFactory clientFactory = new MsgQueueClientFactory();
+
+ private static MsgQueueNodeServerFactory nodeServerFactory = new MsgQueueNodeServerFactory();
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public SettingsFactory getSettingsFactory() {
+ return settingsFactory;
+ }
+
+ @Override
+ public ClientFactory getClientFactory() {
+ return clientFactory;
+ }
+
+ @Override
+ public NodeServerFactory getServerFactory() {
+ return nodeServerFactory;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/MsgQueueConsensusSettingsBuilder.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/MsgQueueConsensusSettingsBuilder.java
new file mode 100644
index 00000000..025ffcef
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/MsgQueueConsensusSettingsBuilder.java
@@ -0,0 +1,253 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.MsgQueueConsensusSettingsBuilder
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 下午1:46
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq;
+
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.consensus.ConsensusSettingsBuilder;
+import com.jd.blockchain.consensus.NodeSettings;
+import com.jd.blockchain.consensus.mq.config.MsgQueueBlockConfig;
+import com.jd.blockchain.consensus.mq.config.MsgQueueConsensusConfig;
+import com.jd.blockchain.consensus.mq.config.MsgQueueNetworkConfig;
+import com.jd.blockchain.consensus.mq.config.MsgQueueNodeConfig;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings;
+import com.jd.blockchain.crypto.AddressEncoding;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.tools.keygen.KeyGenCommand;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.PropertiesUtils;
+import com.jd.blockchain.utils.codec.Base58Utils;
+import com.jd.blockchain.utils.io.BytesUtils;
+import com.jd.blockchain.utils.io.FileUtils;
+import com.jd.blockchain.utils.net.NetworkAddress;
+
+import org.springframework.core.io.ClassPathResource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueConsensusSettingsBuilder implements ConsensusSettingsBuilder {
+
+ private static final String DEFAULT_TOPIC_TX = "tx-topic";
+
+ private static final String DEFAULT_TOPIC_BL = "bl-topic";
+
+ private static final String DEFAULT_TOPIC_MSG = "msg-topic";
+
+ private static final int DEFAULT_TXSIZE = 1000;
+
+ private static final int DEFAULT_MAXDELAY = 1000;
+
+ /**
+ *
+ */
+ private static final String CONFIG_TEMPLATE_FILE = "mq.config";
+
+ /**
+ * 参数键:节点数量;
+ */
+ public static final String SERVER_NUM_KEY = "system.servers.num";
+
+ /**
+ * 参数键格式:节点公钥;
+ */
+ public static final String PUBKEY_PATTERN = "system.server.%s.pubkey";
+
+ public static final String MSG_QUEUE_SERVER = "system.msg.queue.server";
+
+ public static final String MSG_QUEUE_TOPIC_TX = "system.msg.queue.topic.tx";
+
+ public static final String MSG_QUEUE_TOPIC_BL = "system.msg.queue.topic.bl";
+
+ public static final String MSG_QUEUE_TOPIC_MSG = "system.msg.queue.topic.msg";
+
+ public static final String MSG_QUEUE_BLOCK_TXSIZE = "system.msg.queue.block.txsize";
+
+ public static final String MSG_QUEUE_BLOCK_MAXDELAY = "system.msg.queue.block.maxdelay";
+
+ private static Properties CONFIG_TEMPLATE;
+
+ static {
+ if (FileUtils.existFile(CONFIG_TEMPLATE_FILE)) {
+ ClassPathResource configResource = new ClassPathResource(CONFIG_TEMPLATE_FILE);
+ try {
+ try (InputStream in = configResource.getInputStream()) {
+ CONFIG_TEMPLATE = PropertiesUtils.load(in, BytesUtils.DEFAULT_CHARSET);
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+ }
+
+ @Override
+ public MsgQueueConsensusSettings createSettings(Properties props) {
+ MsgQueueNetworkConfig networkConfig = new MsgQueueNetworkConfig();
+ Properties resolvingProps = PropertiesUtils.cloneFrom(props);
+
+ String server = PropertiesUtils.getProperty(resolvingProps, MSG_QUEUE_SERVER, true);
+ if (server == null || server.length()<= 0) {
+ throw new IllegalArgumentException(String.format("Property[%s] is empty!", MSG_QUEUE_SERVER));
+ }
+ networkConfig.setServer(server)
+ .setTxTopic(initProp(resolvingProps, MSG_QUEUE_TOPIC_TX, DEFAULT_TOPIC_TX))
+ .setBlTopic(initProp(resolvingProps, MSG_QUEUE_TOPIC_BL, DEFAULT_TOPIC_BL))
+ .setMsgTopic(initProp(resolvingProps, MSG_QUEUE_TOPIC_MSG, DEFAULT_TOPIC_MSG))
+ ;
+
+ MsgQueueBlockConfig blockConfig = new MsgQueueBlockConfig()
+ .setTxSizePerBlock(initProp(resolvingProps, MSG_QUEUE_BLOCK_TXSIZE, DEFAULT_TXSIZE))
+ .setMaxDelayMilliSecondsPerBlock(initProp(resolvingProps, MSG_QUEUE_BLOCK_MAXDELAY, DEFAULT_MAXDELAY))
+ ;
+
+ MsgQueueConsensusConfig consensusConfig = new MsgQueueConsensusConfig()
+ .setBlockSettings(blockConfig)
+ .setNetworkSettings(networkConfig)
+ ;
+ // load node settings
+ int serversNum = PropertiesUtils.getInt(resolvingProps, SERVER_NUM_KEY);
+ for (int i = 0; i < serversNum; i++) {
+ int id = i;
+
+ String keyOfPubkey = nodeKey(PUBKEY_PATTERN, id);
+
+ String base58PubKey = PropertiesUtils.getRequiredProperty(resolvingProps, keyOfPubkey);
+ PubKey pubKey = KeyGenCommand.decodePubKey(base58PubKey);
+
+// PubKey pubKey = new PubKey(Base58Utils.decode(base58PubKey));
+ resolvingProps.remove(keyOfPubkey);
+ Bytes address = AddressEncoding.generateAddress(pubKey);
+
+ String networkAddress = address.toBase58();
+ MsgQueueNodeConfig nodeConfig = new MsgQueueNodeConfig()
+ .setAddress(networkAddress)
+ .setPubKey(pubKey)
+ ;
+ consensusConfig.addNodeSettings(nodeConfig);
+ }
+ return consensusConfig;
+ }
+
+ @Override
+ public Properties createPropertiesTemplate() {
+ return PropertiesUtils.cloneFrom(CONFIG_TEMPLATE);
+ }
+
+ @Override
+ public void writeSettings(ConsensusSettings settings, Properties props) {
+
+ if (!(settings instanceof MsgQueueConsensusSettings)) {
+ throw new IllegalArgumentException("ConsensusSettings data isn't supported! Accept MsgQueueConsensusSettings only!");
+ }
+
+ MsgQueueConsensusSettings consensusSettings = (MsgQueueConsensusSettings) settings;
+
+ MsgQueueNetworkSettings networkSettings = consensusSettings.getNetworkSettings();
+ if (networkSettings == null || networkSettings.getServer() == null || networkSettings.getServer().length() <= 0) {
+ throw new IllegalArgumentException("MsgQueue Consensus server is empty!");
+ }
+
+ String server = networkSettings.getServer();
+ props.setProperty(MSG_QUEUE_SERVER, server);
+
+ String txTopic = networkSettings.getTxTopic();
+ if (txTopic == null || txTopic.length() <= 0) {
+ txTopic = DEFAULT_TOPIC_TX;
+ }
+ props.setProperty(MSG_QUEUE_TOPIC_TX, txTopic);
+
+ String blTopic = networkSettings.getBlTopic();
+ if (blTopic == null || blTopic.length() <= 0) {
+ blTopic = DEFAULT_TOPIC_BL;
+ }
+ props.setProperty(MSG_QUEUE_TOPIC_BL, blTopic);
+
+ String msgTopic = networkSettings.getMsgTopic();
+ if (msgTopic == null || msgTopic.length() <= 0) {
+ msgTopic = DEFAULT_TOPIC_MSG;
+ }
+ props.setProperty(MSG_QUEUE_TOPIC_MSG, msgTopic);
+
+ MsgQueueBlockSettings blockSettings = consensusSettings.getBlockSettings();
+ if (blockSettings == null) {
+ props.setProperty(MSG_QUEUE_BLOCK_TXSIZE, DEFAULT_TXSIZE + "");
+ props.setProperty(MSG_QUEUE_BLOCK_MAXDELAY, DEFAULT_MAXDELAY + "");
+ } else {
+ int txSize = blockSettings.getTxSizePerBlock();
+ long maxDelay = blockSettings.getMaxDelayMilliSecondsPerBlock();
+ props.setProperty(MSG_QUEUE_BLOCK_TXSIZE, txSize + "");
+ props.setProperty(MSG_QUEUE_BLOCK_MAXDELAY, maxDelay + "");
+ }
+
+
+// int serversNum = PropertiesUtils.getInt(props, SERVER_NUM_KEY);
+// if (serversNum > 0) {
+// for (int i = 0; i < serversNum; i++) {
+// int id = i;
+// String keyOfPubkey = nodeKey(PUBKEY_PATTERN, id);
+// props.remove(keyOfPubkey);
+//
+// String keyOfHost = nodeKey(CONSENSUS_HOST_PATTERN, id);
+// props.remove(keyOfHost);
+// }
+// }
+//
+// NodeSettings[] nodesSettings = consensusSettings.getNodes();
+// serversNum = nodesSettings.length;
+// props.setProperty(SERVER_NUM_KEY, serversNum + "");
+//
+// for (int i = 0; i < serversNum; i++) {
+// MsgQueueNodeSettings mqns = (MsgQueueNodeSettings) nodesSettings[i];
+// int id = i;
+// String keyOfPubkey = nodeKey(PUBKEY_PATTERN, id);
+// props.setProperty(keyOfPubkey, mqns.getPubKey().toBase58());
+//
+// String keyOfHost = nodeKey(CONSENSUS_HOST_PATTERN, id);
+// props.setProperty(keyOfHost, mqns.getAddress() == null ? "" : mqns.getAddress());
+// }
+ }
+
+ private String initProp(Properties resolvingProps, String key, String defaultVal) {
+ try {
+ String value = PropertiesUtils.getProperty(resolvingProps, key, true);
+ if (value == null || value.length() <= 0) {
+ value = defaultVal;
+ }
+ return value;
+ } catch (Exception e) {
+ return defaultVal;
+ }
+ }
+
+ private int initProp(Properties resolvingProps, String key, int defaultVal) {
+ try {
+ int value = PropertiesUtils.getInt(resolvingProps, key);
+ if (value <= 0) {
+ value = defaultVal;
+ }
+ return value;
+ } catch (Exception e) {
+ return defaultVal;
+ }
+ }
+
+ private static String nodeKey(String pattern, int id) {
+ return String.format(pattern, id);
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/DefaultMessageTransmitter.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/DefaultMessageTransmitter.java
new file mode 100644
index 00000000..61333cfc
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/DefaultMessageTransmitter.java
@@ -0,0 +1,311 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.client.DefaultMessageTransmitter
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 下午3:05
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.client;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.jd.blockchain.consensus.event.EventEntity;
+import com.lmax.disruptor.EventHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jd.blockchain.consensus.MessageService;
+import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer;
+import com.jd.blockchain.consensus.mq.event.TxBlockedEvent;
+import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer;
+import com.jd.blockchain.consensus.mq.util.MessageConvertUtil;
+import com.jd.blockchain.utils.concurrent.AsyncFuture;
+import com.jd.blockchain.utils.concurrent.CompletableAsyncFuture;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class DefaultMessageTransmitter implements MessageTransmitter, MessageService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DefaultMessageTransmitter.class);
+
+ private final ExecutorService messageExecutorArray = Executors.newFixedThreadPool(10);
+
+// private final ExecutorService blockExecutor = Executors.newSingleThreadExecutor();
+//
+// private final ExecutorService extendExecutor = Executors.newSingleThreadExecutor();
+
+ private final Map messageListeners = new ConcurrentHashMap<>();
+
+ private final BlockEventHandler blockEventHandler = new BlockEventHandler();
+
+ private final ExtendEventHandler extendEventHandler = new ExtendEventHandler();
+
+ private MsgQueueProducer txProducer;
+
+ private MsgQueueProducer msgProducer;
+
+ private MsgQueueConsumer blConsumer;
+
+ private MsgQueueConsumer msgConsumer;
+
+ private boolean isConnected = false;
+
+ public DefaultMessageTransmitter setTxProducer(MsgQueueProducer txProducer) {
+ this.txProducer = txProducer;
+ return this;
+ }
+
+ public DefaultMessageTransmitter setMsgProducer(MsgQueueProducer msgProducer) {
+ this.msgProducer = msgProducer;
+ return this;
+ }
+
+ public DefaultMessageTransmitter setBlConsumer(MsgQueueConsumer blConsumer) {
+ this.blConsumer = blConsumer;
+ return this;
+ }
+
+ public DefaultMessageTransmitter setMsgConsumer(MsgQueueConsumer msgConsumer) {
+ this.msgConsumer = msgConsumer;
+ return this;
+ }
+
+ @Override
+ public AsyncFuture sendOrdered(byte[] message) {
+
+ AsyncFuture messageFuture;
+
+ try {
+ publishMessage(txProducer, message);
+ messageFuture = messageHandle(message);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return messageFuture;
+ }
+
+ @Override
+ public AsyncFuture sendUnordered(byte[] message) {
+ AsyncFuture messageFuture;
+ try {
+ publishMessage(msgProducer, message);
+ messageFuture = messageHandle(message);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return messageFuture;
+ }
+
+ @Override
+ public void connect() throws Exception{
+ if (!isConnected) {
+ this.txProducer.connect();
+ this.blConsumer.connect(blockEventHandler);
+ this.msgProducer.connect();
+ this.msgConsumer.connect(extendEventHandler);
+ isConnected = true;
+ blConsumer.start();
+ msgConsumer.start();
+// blockConsumerListening();
+// extendConsumerListening();
+ }
+ }
+
+ @Override
+ public void publishMessage(MsgQueueProducer producer, byte[] message) throws Exception {
+ producer.publish(message);
+ }
+
+ @Override
+ public void close() {
+ try {
+ txProducer.close();
+ blConsumer.close();
+ msgProducer.close();
+ msgConsumer.close();
+ isConnected = false;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private AsyncFuture messageHandle(byte[] message) throws Exception {
+// 异步回调
+// 需要监听MQ结块的应答
+// 首先需要一个Consumer,在子类已实现
+ String messageKey = messageKey(message);
+ AsyncFuture messageFuture = registerMessageListener(messageKey);
+ return messageFuture;
+ }
+
+ private String messageKey(byte[] message) {
+ return MessageConvertUtil.messageKey(message);
+ }
+
+ private AsyncFuture registerMessageListener(String messageKey) {
+ CompletableAsyncFuture future = new CompletableAsyncFuture<>();
+ MessageListener messageListener = new MessageListener(messageKey, future);
+ messageListener.addListener();
+ return future;
+ }
+
+// private void blockConsumerListening() {
+// // 区块事件由单独一个线程处理
+// blockExecutor.execute(() -> {
+// while(isConnected) {
+// try {
+// byte[] txBlockedEventBytes = blConsumer.start();
+// // 交由事件处理机制来处理
+// if (txBlockedEventBytes != null && txBlockedEventBytes.length > 0) {
+// txBlockedEventHandle(txBlockedEventBytes);
+// }
+// } catch (Exception e) {
+// LOGGER.error("process block listening message exception {}", e.getMessage());
+// }
+// }
+// });
+// }
+
+// private void extendConsumerListening() {
+// extendExecutor.execute(() -> {
+// while (isConnected) {
+// try {
+// byte[] msgBytes = msgConsumer.start();
+// // 交由事件处理机制来处理
+// if (msgBytes != null && msgBytes.length > 0) {
+// extendMessageHandle(msgBytes);
+// }
+// } catch (Exception e) {
+// LOGGER.error("process extend listening message exception {}", e.getMessage());
+// }
+// }
+// });
+// }
+
+ private void txBlockedEventHandle(byte[] bytes) {
+ messageExecutorArray.execute(() -> {
+ if (!this.messageListeners.isEmpty()) {
+ // 首先将字节数组转换为BlockEvent
+ final TxBlockedEvent txBlockedEvent =
+ MessageConvertUtil.convertBytes2TxBlockedEvent(bytes);
+ if (txBlockedEvent != null) {
+ // 需要判断该区块是否需要处理
+ if (isTxBlockedEventNeedManage(txBlockedEvent)) {
+ dealTxBlockedEvent(txBlockedEvent);
+ }
+ }
+ }
+ });
+ }
+
+ private void extendMessageHandle(byte[] message) {
+ messageExecutorArray.execute(() -> {
+ String messageKey = messageKey(message);
+ if (messageListeners.containsKey(messageKey)) {
+ dealExtendMessage(messageKey, message);
+ }
+ });
+ }
+
+ private boolean isTxBlockedEventNeedManage(final TxBlockedEvent txBlockedEvent) {
+ if (this.messageListeners.isEmpty()) {
+ return false;
+ }
+ if (messageListeners.containsKey(txBlockedEvent.getTxKey())) {
+ return true;
+ }
+ // 无须处理区块高度
+ return false;
+ }
+
+ private void dealTxBlockedEvent(final TxBlockedEvent txBlockedEvent) {
+ String txKey = txBlockedEvent.getTxKey();
+ MessageListener txListener = this.messageListeners.get(txKey);
+ if (txListener != null) {
+ txListener.received(txBlockedEvent);
+ this.messageListeners.remove(txKey);
+ }
+ }
+
+ private void dealExtendMessage(final String messageKey, final byte[] message) {
+ MessageListener txListener = this.messageListeners.get(messageKey);
+ if (txListener != null) {
+ txListener.received(message);
+ this.messageListeners.remove(messageKey);
+ }
+ }
+
+ private class MessageListener {
+
+ final String messageKey;
+
+ final CompletableAsyncFuture future;
+
+ final AtomicBoolean isDeal = new AtomicBoolean(false);
+
+ public MessageListener(String messageKey, CompletableAsyncFuture future) {
+ this.messageKey = messageKey;
+ this.future = future;
+ addListener();
+ }
+
+ public void addListener() {
+ synchronized (messageListeners) {
+ messageListeners.put(messageKey, this);
+ }
+ }
+
+ public void received(final TxBlockedEvent txBlockedEvent) {
+ // 期望是false,假设是false则设置为true,成功的情况下表示是第一次
+ byte[] txResp = txBlockedEvent.txResponseBytes();
+ if (txResp != null) {
+ if (isDeal.compareAndSet(false, true)) {
+ //生成对应的交易应答
+ future.complete(txResp);
+ }
+ }
+ }
+
+ public void received(final byte[] message) {
+ // 期望是false,假设是false则设置为true,成功的情况下表示是第一次
+ if (message != null) {
+ if (isDeal.compareAndSet(false, true)) {
+ //生成对应的交易应答
+ future.complete(message);
+ }
+ }
+ }
+ }
+
+ public class BlockEventHandler implements EventHandler> {
+
+ @Override
+ public void onEvent(EventEntity event, long sequence, boolean endOfBatch) throws Exception {
+ byte[] txBlockedEventBytes = event.getEntity();
+ if (txBlockedEventBytes != null && txBlockedEventBytes.length > 0) {
+ txBlockedEventHandle(txBlockedEventBytes);
+ }
+ }
+ }
+
+ public class ExtendEventHandler implements EventHandler> {
+
+ @Override
+ public void onEvent(EventEntity event, long sequence, boolean endOfBatch) throws Exception {
+ byte[] msgBytes = event.getEntity();
+ if (msgBytes != null && msgBytes.length > 0) {
+ extendMessageHandle(msgBytes);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MessageTransmitter.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MessageTransmitter.java
new file mode 100644
index 00000000..73c574e0
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MessageTransmitter.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.client.MessageTransmitter
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 上午11:21
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.client;
+
+
+import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public interface MessageTransmitter {
+
+ void connect() throws Exception;
+
+ void publishMessage(MsgQueueProducer producer, byte[] message) throws Exception;
+
+// void processMsg(byte[] message);
+
+ void close();
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueClientFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueClientFactory.java
new file mode 100644
index 00000000..2158f223
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueClientFactory.java
@@ -0,0 +1,97 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.client.MsgQueueClientFactory
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 上午11:23
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.client;
+
+import com.jd.blockchain.consensus.ClientIncomingSettings;
+import com.jd.blockchain.consensus.ConsensusManageService;
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.consensus.client.ClientFactory;
+import com.jd.blockchain.consensus.client.ClientSettings;
+import com.jd.blockchain.consensus.mq.config.MsgQueueClientConfig;
+import com.jd.blockchain.consensus.mq.config.MsgQueueClientIncomingConfig;
+import com.jd.blockchain.consensus.mq.config.MsgQueueConsensusConfig;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueClientIncomingSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueClientSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings;
+import com.jd.blockchain.crypto.CryptoUtils;
+import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.asymmetric.SignatureDigest;
+import com.jd.blockchain.crypto.asymmetric.SignatureFunction;
+
+import java.lang.reflect.Proxy;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueClientFactory implements ClientFactory {
+
+ @Override
+ public MsgQueueClientIdentification buildAuthId(CryptoKeyPair clientKeyPair) {
+ PubKey pubKey = clientKeyPair.getPubKey();
+ byte[] address = pubKey.toBytes(); // 使用公钥地址作为认证信息
+
+ SignatureFunction signatureFunction = CryptoUtils.sign(pubKey.getAlgorithm());
+ SignatureDigest signatureDigest = signatureFunction.sign(clientKeyPair.getPrivKey(), address);
+
+ MsgQueueClientIdentification mqci = new MsgQueueClientIdentification()
+ .setPubKey(clientKeyPair.getPubKey())
+ .setIdentityInfo(address)
+ .setSignature(signatureDigest)
+ ;
+ return mqci;
+ }
+
+ @Override
+ public MsgQueueClientSettings buildClientSettings(ClientIncomingSettings incomingSettings) {
+
+ MsgQueueClientIncomingSettings mqcic = (MsgQueueClientIncomingSettings)incomingSettings;
+ if (mqcic != null) {
+ return buildClientSettings(mqcic.getClientId(), mqcic.getPubKey(), (MsgQueueConsensusSettings)(mqcic.getConsensusSettings()));
+ }
+ throw new IllegalArgumentException("ClientIncomingSettings data isn't supported! Accept MsgQueueClientIncomingSettings only!");
+ }
+
+ private MsgQueueClientSettings buildClientSettings(int clientId, PubKey pubKey, MsgQueueConsensusSettings mqcs) {
+
+ MsgQueueClientSettings msgQueueClientConfig = new MsgQueueClientConfig()
+ .setId(clientId)
+ .setPubKey(pubKey)
+ .setConsensusSettings(mqcs)
+ ;
+ return msgQueueClientConfig;
+ }
+
+ @Override
+ public ConsensusManageService createManageServiceClient(String[] serviceNodes) {
+ // todo serviceNodes // IP:port
+ return null;
+ }
+
+ @Override
+ public MsgQueueConsensusClient setupClient(ClientSettings settings) {
+ if (settings instanceof MsgQueueClientSettings) {
+ return setupClient((MsgQueueClientSettings)settings);
+ }
+ throw new IllegalArgumentException("ClientSettings data isn't supported! Accept MsgQueueClientSettings only!");
+ }
+
+ private MsgQueueConsensusClient setupClient(MsgQueueClientSettings settings) {
+ MsgQueueConsensusClient mqcc = new MsgQueueConsensusClient()
+ .setClientSettings(settings)
+ .setMsgQueueNetworkSettings(settings.getMsgQueueNetworkSettings())
+ ;
+ mqcc.init();
+ return mqcc;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueClientIdentification.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueClientIdentification.java
new file mode 100644
index 00000000..b34b70b3
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueClientIdentification.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.client.MsgQueueClientIdentification
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 下午2:04
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.client;
+
+import com.jd.blockchain.consensus.ClientIdentification;
+import com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider;
+import com.jd.blockchain.crypto.CryptoUtils;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.asymmetric.SignatureDigest;
+import com.jd.blockchain.crypto.asymmetric.SignatureFunction;
+
+import java.util.Arrays;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueClientIdentification implements ClientIdentification {
+
+ private byte[] identityInfo;
+
+ private PubKey pubKey;
+
+ private SignatureDigest signature;
+
+ public MsgQueueClientIdentification() {
+ }
+
+ public MsgQueueClientIdentification(ClientIdentification clientIdentification) {
+ identityInfo = clientIdentification.getIdentityInfo();
+ pubKey = clientIdentification.getPubKey();
+ signature = clientIdentification.getSignature();
+ }
+
+ public MsgQueueClientIdentification setIdentityInfo(byte[] identityInfo) {
+ this.identityInfo = identityInfo;
+ return this;
+ }
+
+ public MsgQueueClientIdentification setPubKey(PubKey pubKey) {
+ this.pubKey = pubKey;
+ return this;
+ }
+
+ public MsgQueueClientIdentification setSignature(SignatureDigest signature) {
+ this.signature = signature;
+ return this;
+ }
+
+ @Override
+ public byte[] getIdentityInfo() {
+ return this.identityInfo;
+ }
+
+ @Override
+ public PubKey getPubKey() {
+ return this.pubKey;
+ }
+
+ @Override
+ public SignatureDigest getSignature() {
+ return this.signature;
+ }
+
+ @Override
+ public String getProviderName() {
+ return MsgQueueConsensusProvider.NAME;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueConsensusClient.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueConsensusClient.java
new file mode 100644
index 00000000..6432dcb3
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueConsensusClient.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.client.MsgQueueConsensusClient
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 下午3:23
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.client;
+
+import com.jd.blockchain.consensus.MessageService;
+import com.jd.blockchain.consensus.client.ClientSettings;
+import com.jd.blockchain.consensus.client.ConsensusClient;
+import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer;
+import com.jd.blockchain.consensus.mq.factory.MsgQueueFactory;
+import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueClientSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueConsensusClient implements ConsensusClient {
+
+ private boolean isConnected;
+
+ private DefaultMessageTransmitter transmitter;
+
+ private MsgQueueNetworkSettings msgQueueNetworkSettings;
+
+ private MsgQueueClientSettings clientSettings;
+
+ public MsgQueueConsensusClient setClientSettings(MsgQueueClientSettings clientSettings) {
+ this.clientSettings = clientSettings;
+ return this;
+ }
+
+ public MsgQueueConsensusClient setMsgQueueNetworkSettings(MsgQueueNetworkSettings msgQueueNetworkSettings) {
+ this.msgQueueNetworkSettings = msgQueueNetworkSettings;
+ return this;
+ }
+
+ public void init() {
+ String server = msgQueueNetworkSettings.getServer();
+ String txTopic = msgQueueNetworkSettings.getTxTopic();
+ String blTopic = msgQueueNetworkSettings.getBlTopic();
+ String msgTopic = msgQueueNetworkSettings.getMsgTopic();
+
+ MsgQueueProducer txProducer = MsgQueueFactory.newProducer(server, txTopic);
+ MsgQueueProducer msgProducer = MsgQueueFactory.newProducer(server, msgTopic);
+ MsgQueueConsumer blConsumer = MsgQueueFactory.newConsumer(server, blTopic);
+ MsgQueueConsumer msgConsumer = MsgQueueFactory.newConsumer(server, msgTopic);
+
+ transmitter = new DefaultMessageTransmitter()
+ .setTxProducer(txProducer)
+ .setMsgProducer(msgProducer)
+ .setBlConsumer(blConsumer)
+ .setMsgConsumer(msgConsumer)
+ ;
+ }
+
+ @Override
+ public MessageService getMessageService() {
+ return transmitter;
+ }
+
+ @Override
+ public ClientSettings getSettings() {
+ return clientSettings;
+ }
+
+ @Override
+ public boolean isConnected() {
+ return isConnected;
+ }
+
+ @Override
+ public synchronized void connect() {
+ if (!isConnected) {
+ try {
+ this.transmitter.connect();
+ isConnected = true;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void close() {
+ if (isConnected) {
+ transmitter.close();
+ isConnected = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueBlockConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueBlockConfig.java
new file mode 100644
index 00000000..7a8a7560
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueBlockConfig.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueBlockConfig
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/13 下午2:57
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.config;
+
+import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings;
+
+import java.lang.reflect.Method;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/13
+ * @since 1.0.0
+ */
+
+public class MsgQueueBlockConfig implements MsgQueueBlockSettings {
+
+ private int txSizePerBlock;
+
+ private long maxDelayMilliSecondsPerBlock;
+
+ @Override
+ public int getTxSizePerBlock() {
+ return txSizePerBlock;
+ }
+
+ public MsgQueueBlockConfig setTxSizePerBlock(int txSizePerBlock) {
+ this.txSizePerBlock = txSizePerBlock;
+ return this;
+ }
+
+ @Override
+ public long getMaxDelayMilliSecondsPerBlock() {
+ return maxDelayMilliSecondsPerBlock;
+ }
+
+ public MsgQueueBlockConfig setMaxDelayMilliSecondsPerBlock(long maxDelayMilliSecondsPerBlock) {
+ this.maxDelayMilliSecondsPerBlock = maxDelayMilliSecondsPerBlock;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueClientConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueClientConfig.java
new file mode 100644
index 00000000..5fec7a69
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueClientConfig.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueClientConfig
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 下午2:23
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.config;
+
+import com.jd.blockchain.consensus.mq.settings.MsgQueueClientSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueClientConfig implements MsgQueueClientSettings {
+
+ private int id;
+
+ private PubKey pubKey;
+
+ private MsgQueueConsensusSettings consensusSettings;
+
+ public MsgQueueClientConfig setId(int id) {
+ this.id = id;
+ return this;
+ }
+
+ public MsgQueueClientConfig setPubKey(PubKey pubKey) {
+ this.pubKey = pubKey;
+ return this;
+ }
+
+ public MsgQueueClientConfig setConsensusSettings(MsgQueueConsensusSettings consensusSettings) {
+ this.consensusSettings = consensusSettings;
+ return this;
+ }
+
+ @Override
+ public int getClientId() {
+ return this.id;
+ }
+
+ @Override
+ public PubKey getClientPubKey() {
+ return this.pubKey;
+ }
+
+ @Override
+ public MsgQueueConsensusSettings getConsensusSettings() {
+ return this.consensusSettings;
+ }
+
+ @Override
+ public MsgQueueNetworkSettings getMsgQueueNetworkSettings() {
+ return this.consensusSettings.getNetworkSettings();
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueClientIncomingConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueClientIncomingConfig.java
new file mode 100644
index 00000000..8e947f26
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueClientIncomingConfig.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.config.MsgQueueClientIncomingConfig
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 上午11:50
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.config;
+
+import com.jd.blockchain.consensus.ClientIncomingSettings;
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueClientIncomingSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+
+import java.lang.reflect.Method;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueClientIncomingConfig implements MsgQueueClientIncomingSettings {
+
+ private int clientId;
+
+ private PubKey pubKey;
+
+ private MsgQueueConsensusSettings consensusSettings;
+
+ public MsgQueueClientIncomingConfig setConsensusSettings(MsgQueueConsensusSettings consensusSettings) {
+ this.consensusSettings = consensusSettings;
+ return this;
+ }
+
+ public MsgQueueClientIncomingConfig setClientId(int clientId) {
+ this.clientId = clientId;
+ return this;
+ }
+
+ public MsgQueueClientIncomingConfig setPubKey(PubKey pubKey) {
+ this.pubKey = pubKey;
+ return this;
+ }
+
+ @Override
+ public int getClientId() {
+ return this.clientId;
+ }
+
+ @Override
+ public String getProviderName() {
+ return MsgQueueConsensusProvider.NAME;
+ }
+
+ @Override
+ public MsgQueueConsensusSettings getConsensusSettings() {
+ return this.consensusSettings;
+ }
+
+ @Override
+ public PubKey getPubKey() {
+ return pubKey;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueConsensusConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueConsensusConfig.java
new file mode 100644
index 00000000..526ac603
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueConsensusConfig.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.config.MsgQueueConsensusConfig
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 上午11:26
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.config;
+
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.consensus.NodeSettings;
+import com.jd.blockchain.consensus.mq.settings.*;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 设置消息队列的信息
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueConsensusConfig implements MsgQueueConsensusSettings {
+
+ private List nodeSettingsList = new ArrayList<>();
+
+ private MsgQueueNetworkSettings networkSettings;
+
+ private MsgQueueBlockSettings blockSettings;
+
+ public MsgQueueConsensusConfig addNodeSettings(MsgQueueNodeSettings nodeSettings) {
+ nodeSettingsList.add(nodeSettings);
+ return this;
+ }
+
+ public MsgQueueConsensusConfig setNetworkSettings(MsgQueueNetworkSettings networkSettings) {
+ this.networkSettings = networkSettings;
+ return this;
+ }
+
+ public MsgQueueConsensusConfig setBlockSettings(MsgQueueBlockSettings blockSettings) {
+ this.blockSettings = blockSettings;
+ return this;
+ }
+
+ @Override
+ public NodeSettings[] getNodes() {
+ return nodeSettingsList.toArray(new NodeSettings[nodeSettingsList.size()]);
+ }
+
+ @Override
+ public MsgQueueNetworkSettings getNetworkSettings() {
+ return networkSettings;
+ }
+
+ @Override
+ public MsgQueueBlockSettings getBlockSettings() {
+ return blockSettings;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueNetworkConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueNetworkConfig.java
new file mode 100644
index 00000000..d73c6b65
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueNetworkConfig.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueNetworkConfig
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 下午4:55
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.config;
+
+import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings;
+
+import java.lang.reflect.Method;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueNetworkConfig implements MsgQueueNetworkSettings {
+
+ private String server;
+
+ private String txTopic;
+
+ private String blTopic;
+
+ private String msgTopic;
+
+ public MsgQueueNetworkConfig setServer(String server) {
+ this.server = server;
+ return this;
+ }
+
+ public MsgQueueNetworkConfig setTxTopic(String txTopic) {
+ this.txTopic = txTopic;
+ return this;
+ }
+
+ public MsgQueueNetworkConfig setBlTopic(String blTopic) {
+ this.blTopic = blTopic;
+ return this;
+ }
+
+ public MsgQueueNetworkConfig setMsgTopic(String msgTopic) {
+ this.msgTopic = msgTopic;
+ return this;
+ }
+
+ @Override
+ public String getServer() {
+ return server;
+ }
+
+ @Override
+ public String getTxTopic() {
+ return txTopic;
+ }
+
+ @Override
+ public String getBlTopic() {
+ return blTopic;
+ }
+
+ @Override
+ public String getMsgTopic() {
+ return msgTopic;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueNodeConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueNodeConfig.java
new file mode 100644
index 00000000..c7cc03d7
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueNodeConfig.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.config.MsgQueueNodeConfig
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 上午11:33
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.config;
+
+import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+
+/**
+ * peer节点IP
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueNodeConfig implements MsgQueueNodeSettings {
+
+ private String address;
+
+ private PubKey pubKey;
+
+ public MsgQueueNodeConfig setAddress(String address) {
+ this.address = address;
+ return this;
+ }
+
+ public MsgQueueNodeConfig setPubKey(PubKey pubKey) {
+ this.pubKey = pubKey;
+ return this;
+ }
+
+ @Override
+ public String getAddress() {
+ return this.address;
+ }
+
+ @Override
+ public PubKey getPubKey() {
+ return this.pubKey;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueServerConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueServerConfig.java
new file mode 100644
index 00000000..7b7a79b9
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueServerConfig.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.config.MsgQueueServerConfig
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 上午11:32
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.config;
+
+import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueServerSettings;
+
+/**
+ * peer节点配置
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueServerConfig implements MsgQueueServerSettings {
+
+ private MsgQueueBlockSettings blockSettings;
+
+ private MsgQueueConsensusSettings consensusSettings;
+
+ private MsgQueueNodeSettings nodeSettings;
+
+ private String realmName;
+
+ public MsgQueueServerConfig setRealmName(String realmName) {
+ this.realmName = realmName;
+ return this;
+ }
+
+ public MsgQueueServerConfig setBlockSettings(MsgQueueBlockSettings blockSettings) {
+ this.blockSettings = blockSettings;
+ return this;
+ }
+
+ public MsgQueueServerConfig setConsensusSettings(MsgQueueConsensusSettings consensusSettings) {
+ this.consensusSettings = consensusSettings;
+ return setBlockSettings(consensusSettings.getBlockSettings());
+ }
+
+ public MsgQueueServerConfig setNodeSettings(MsgQueueNodeSettings nodeSettings) {
+ this.nodeSettings = nodeSettings;
+ return this;
+ }
+
+ @Override
+ public String getRealmName() {
+ return this.realmName;
+ }
+
+ @Override
+ public MsgQueueNodeSettings getReplicaSettings() {
+ return nodeSettings;
+ }
+
+ @Override
+ public MsgQueueBlockSettings getBlockSettings() {
+ return blockSettings;
+ }
+
+ @Override
+ public MsgQueueConsensusSettings getConsensusSettings() {
+ return consensusSettings;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueSettingsFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueSettingsFactory.java
new file mode 100644
index 00000000..e87adb9e
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueSettingsFactory.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.config.MsgQueueSettingsFactory
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 上午11:49
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.config;
+
+import com.jd.blockchain.binaryproto.BinaryEncodingUtils;
+import com.jd.blockchain.binaryproto.DataContractRegistry;
+import com.jd.blockchain.consensus.ClientIncomingSettings;
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.consensus.NodeSettings;
+import com.jd.blockchain.consensus.SettingsFactory;
+import com.jd.blockchain.consensus.mq.MsgQueueConsensusSettingsBuilder;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueClientIncomingSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings;
+import com.jd.blockchain.utils.io.BytesEncoder;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueSettingsFactory implements SettingsFactory {
+
+ static {
+ DataContractRegistry.register(NodeSettings.class);
+
+ DataContractRegistry.register(MsgQueueNodeSettings.class);
+
+ DataContractRegistry.register(ConsensusSettings.class);
+
+ DataContractRegistry.register(MsgQueueConsensusSettings.class);
+
+ DataContractRegistry.register(MsgQueueNetworkSettings.class);
+
+ DataContractRegistry.register(MsgQueueBlockSettings.class);
+
+ DataContractRegistry.register(MsgQueueClientIncomingSettings.class);
+
+ DataContractRegistry.register(ClientIncomingSettings.class);
+ }
+
+ private static final MsgQueueConsensusSettingsEncoder MQCS_ENCODER = new MsgQueueConsensusSettingsEncoder();
+
+ private static final MsgQueueClientIncomingSettingsEncoder MQCIS_ENCODER = new MsgQueueClientIncomingSettingsEncoder();
+
+ private static final MsgQueueConsensusSettingsBuilder BUILDER = new MsgQueueConsensusSettingsBuilder();
+
+ @Override
+ public MsgQueueConsensusSettingsBuilder getConsensusSettingsBuilder() {
+ return BUILDER;
+ }
+
+ @Override
+ public BytesEncoder getConsensusSettingsEncoder() {
+ return MQCS_ENCODER;
+ }
+
+ @Override
+ public BytesEncoder getIncomingSettingsEncoder() {
+ return MQCIS_ENCODER;
+ }
+
+ private static class MsgQueueConsensusSettingsEncoder implements BytesEncoder{
+
+ @Override
+ public byte[] encode(ConsensusSettings data) {
+ if (data instanceof MsgQueueConsensusSettings) {
+ return BinaryEncodingUtils.encode(data, MsgQueueConsensusSettings.class);
+ }
+ throw new IllegalArgumentException("Settings data isn't supported! Accept MsgQueueConsensusSettings only!");
+ }
+
+ @Override
+ public MsgQueueConsensusSettings decode(byte[] bytes) {
+ return BinaryEncodingUtils.decodeAs(bytes, MsgQueueConsensusSettings.class);
+ }
+ }
+
+ private static class MsgQueueClientIncomingSettingsEncoder implements BytesEncoder{
+
+ @Override
+ public byte[] encode(ClientIncomingSettings data) {
+ if (data instanceof MsgQueueClientIncomingSettings) {
+ return BinaryEncodingUtils.encode(data, MsgQueueClientIncomingSettings.class);
+ }
+ throw new IllegalArgumentException("Settings data isn't supported! Accept MsgQueueClientIncomingSettings only!");
+ }
+
+ @Override
+ public MsgQueueClientIncomingSettings decode(byte[] bytes) {
+ return BinaryEncodingUtils.decodeAs(bytes, MsgQueueClientIncomingSettings.class);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/AbstractConsumer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/AbstractConsumer.java
new file mode 100644
index 00000000..9c39d8e9
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/AbstractConsumer.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.consumer.AbstractConsumer
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/29 下午12:31
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.consumer;
+
+import com.jd.blockchain.consensus.event.EventEntity;
+import com.jd.blockchain.consensus.event.EventProducer;
+import com.jd.blockchain.consensus.mq.exchange.BytesEventFactory;
+import com.jd.blockchain.consensus.mq.exchange.BytesEventProducer;
+import com.lmax.disruptor.BlockingWaitStrategy;
+import com.lmax.disruptor.EventHandler;
+import com.lmax.disruptor.RingBuffer;
+import com.lmax.disruptor.dsl.Disruptor;
+import com.lmax.disruptor.dsl.ProducerType;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/29
+ * @since 1.0.0
+ */
+
+public abstract class AbstractConsumer implements MsgQueueConsumer {
+
+ protected EventProducer eventProducer;
+
+ protected void initEventHandler(EventHandler eventHandler) {
+ Disruptor> disruptor =
+ new Disruptor<>(new BytesEventFactory(),
+ BytesEventFactory.BUFFER_SIZE, r -> {
+ return new Thread(r);
+ }, ProducerType.SINGLE, new BlockingWaitStrategy());
+
+ disruptor.handleEventsWith(eventHandler);
+ disruptor.start();
+ RingBuffer> ringBuffer = disruptor.getRingBuffer();
+ this.eventProducer = new BytesEventProducer(ringBuffer);
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/MsgQueueConsumer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/MsgQueueConsumer.java
new file mode 100644
index 00000000..43908abb
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/MsgQueueConsumer.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: MsgQueueConsumer
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午10:38
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.consumer;
+
+import com.lmax.disruptor.EventHandler;
+
+import java.io.Closeable;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public interface MsgQueueConsumer extends Closeable {
+
+ void connect(EventHandler eventHandler) throws Exception;
+
+ void start() throws Exception;
+}
+
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/NatsConsumer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/NatsConsumer.java
new file mode 100644
index 00000000..0df9c6dd
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/NatsConsumer.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.sdk.nats.RabbitConsumer
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午10:40
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.consumer;
+
+import com.jd.blockchain.utils.ConsoleUtils;
+import com.lmax.disruptor.EventHandler;
+import io.nats.client.*;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class NatsConsumer extends AbstractConsumer implements MsgQueueConsumer {
+
+ private final ExecutorService msgListener = Executors.newSingleThreadExecutor();
+
+ private Connection nc;
+
+ private Subscription sub;
+
+ private String server;
+
+ private String topic;
+
+ public NatsConsumer(String server, String topic) {
+ this.server = server;
+ this.topic = topic;
+ }
+
+ @Override
+ public void connect(EventHandler eventHandler) throws Exception {
+ initEventHandler(eventHandler);
+ Options options = new Options.Builder().server(server).noReconnect().build();
+ this.nc = Nats.connect(options);
+ this.sub = nc.subscribe(topic);
+ this.nc.flush(Duration.ZERO);
+ ConsoleUtils.info("[*] NatsConsumer[%s, %s] connect success !!!", this.server, this.topic);
+ }
+
+ @Override
+ public void start() {
+ msgListener.execute(() -> {
+ for (;;) {
+ try {
+ Message msg = this.sub.nextMessage(Duration.ZERO);
+ eventProducer.publish(msg.getData());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ nc.close();
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/RabbitConsumer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/RabbitConsumer.java
new file mode 100644
index 00000000..5076537a
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/RabbitConsumer.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.sdk.nats.RabbitConsumer
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午10:40
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.consumer;
+
+import com.jd.blockchain.consensus.mq.factory.RabbitFactory;
+import com.jd.blockchain.utils.ConsoleUtils;
+import com.lmax.disruptor.EventHandler;
+import com.rabbitmq.client.*;
+
+import java.io.IOException;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class RabbitConsumer extends AbstractConsumer implements MsgQueueConsumer {
+
+ private Connection connection;
+
+ private Channel channel;
+
+ private String exchangeName;
+
+ private String server;
+
+ private String queueName;
+
+ public RabbitConsumer(String server, String topic) {
+ this.server = server;
+ this.exchangeName = topic;
+ }
+
+ private void rabbitConsumerHandle() throws Exception {
+ rabbitConsumerHandleByQueue();
+ }
+
+ private void rabbitConsumerHandleByQueue() throws IOException {
+ DefaultConsumer consumer = new DefaultConsumer(channel) {
+ @Override
+ public void handleDelivery(String consumerTag, Envelope envelope,
+ AMQP.BasicProperties properties, byte[] body) {
+ // 此处将收到的消息加入队列即可
+ try {
+ eventProducer.publish(body);
+ channel.basicAck(envelope.getDeliveryTag(), false);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ this.channel.basicConsume(this.queueName, false, consumer);
+ }
+
+ @Override
+ public void connect(EventHandler eventHandler) throws Exception {
+ initEventHandler(eventHandler);
+ ConnectionFactory factory = RabbitFactory.initConnectionFactory(server);
+ connection = factory.newConnection();
+ channel = connection.createChannel();
+
+ channel.exchangeDeclare(this.exchangeName, "fanout");
+ queueName = channel.queueDeclare().getQueue();
+ channel.queueBind(queueName, this.exchangeName, "");
+ channel.basicQos(8);
+
+ ConsoleUtils.info("[*] RabbitConsumer[%s, %s] connect success !!!", this.server, this.exchangeName);
+ }
+
+ @Override
+ public void start() throws Exception {
+ rabbitConsumerHandle();
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ this.channel.close();
+ this.connection.close();
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/BlockEvent.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/BlockEvent.java
new file mode 100644
index 00000000..03b92113
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/BlockEvent.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: BlockEvent
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/20 上午11:32
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.event;
+
+import com.jd.blockchain.consensus.mq.util.MessageConvertUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/20
+ * @since 1.0.0
+ */
+
+public class BlockEvent {
+
+ private Map txMap = new HashMap<>();
+
+ public Map getTxMap() {
+ return txMap;
+ }
+
+ public void setTxMap(Map txMap) {
+ this.txMap = txMap;
+ }
+
+ public void put(String txKey, String txResp) {
+ txMap.put(txKey, txResp);
+ }
+
+ public void put(String txKey, byte[] txResp) {
+ put(txKey, MessageConvertUtil.base64Encode(txResp));
+ }
+
+ public String getTxResp(String txKey) {
+ return txMap.get(txKey);
+ }
+
+ public byte[] getTxRespBytes(String txKey) {
+ String txResp = getTxResp(txKey);
+ if (txResp != null && txResp.length() > 0) {
+ // 字符串转字节数组
+ return MessageConvertUtil.base64Decode(txResp);
+ }
+ return null;
+ }
+
+ public boolean containTxResp(String txKey) {
+ return txMap.containsKey(txKey);
+ }
+
+ public boolean isEmpty() {
+ if (txMap == null) return true;
+ return txMap.isEmpty();
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/MessageEvent.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/MessageEvent.java
new file mode 100644
index 00000000..94710a43
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/MessageEvent.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.peer.consensus.MessageEvent
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/23 上午11:45
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.event;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/23
+ * @since 1.0.0
+ */
+
+public class MessageEvent {
+
+ String messageKey;
+
+ byte[] message;
+
+ public MessageEvent(String messageKey, byte[] message) {
+ this.messageKey = messageKey;
+ this.message = message;
+ }
+
+ public String getMessageKey() {
+ return messageKey;
+ }
+
+ public void setMessageKey(String messageKey) {
+ this.messageKey = messageKey;
+ }
+
+ public byte[] getMessage() {
+ return message;
+ }
+
+ public void setMessage(byte[] message) {
+ this.message = message;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/TxBlockedEvent.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/TxBlockedEvent.java
new file mode 100644
index 00000000..5c0515d6
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/TxBlockedEvent.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: BlockEvent
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/20 上午11:32
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.event;
+
+
+import com.jd.blockchain.consensus.mq.util.MessageConvertUtil;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/20
+ * @since 1.0.0
+ */
+
+public class TxBlockedEvent {
+
+ private String txKey;
+
+ private String transaction;
+
+ public TxBlockedEvent() {
+ }
+
+ public TxBlockedEvent(String txKey, String transaction) {
+ this.txKey = txKey;
+ this.transaction = transaction;
+ }
+
+ public void setTxKey(String txKey) {
+ this.txKey = txKey;
+ }
+
+ public void setTransaction(String transaction) {
+ this.transaction = transaction;
+ }
+
+ public String getTxKey() {
+ return txKey;
+ }
+
+ public String getTransaction() {
+ return transaction;
+ }
+
+ public byte[] txResponseBytes() {
+ if (transaction != null && transaction.length() > 0) {
+ // 字符串转字节数组
+ return MessageConvertUtil.base64Decode(transaction);
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/BytesEventFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/BytesEventFactory.java
new file mode 100644
index 00000000..557c2649
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/BytesEventFactory.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.rabbitmq.nats.consensus.disruptor.ExchangeEventFactory
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 上午10:48
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.exchange;
+
+import com.jd.blockchain.consensus.event.EventEntity;
+import com.lmax.disruptor.EventFactory;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class BytesEventFactory implements EventFactory> {
+
+ public static final int BUFFER_SIZE = 256 * 1024;
+// public static final int BUFFER_SIZE = 8 * 1024;
+
+ @Override
+ public EventEntity newInstance() {
+ return new EventEntity<>();
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/BytesEventProducer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/BytesEventProducer.java
new file mode 100644
index 00000000..0421e425
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/BytesEventProducer.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.rabbitmq.nats.consensus.disruptor.ExchangeEventProducer
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 上午10:50
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.exchange;
+
+import com.jd.blockchain.consensus.event.EventEntity;
+import com.jd.blockchain.consensus.event.EventProducer;
+import com.lmax.disruptor.RingBuffer;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class BytesEventProducer implements EventProducer {
+
+ private final RingBuffer> ringBuffer;
+
+ public BytesEventProducer(RingBuffer> ringBuffer) {
+ this.ringBuffer = ringBuffer;
+ }
+
+ @Override
+ public void publish(byte[] entity) {
+ long sequence = ringBuffer.next();
+ try {
+ EventEntity event = ringBuffer.get(sequence);
+ event.setEntity(entity);
+ } finally {
+ this.ringBuffer.publish(sequence);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEntityFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEntityFactory.java
new file mode 100644
index 00000000..39c7494a
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEntityFactory.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.sdk.ExchangeEntityFactory
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午4:08
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.exchange;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class ExchangeEntityFactory {
+
+ public static ExchangeEventInnerEntity newBlockInstance() {
+ return new ExchangeEventInnerEntity(ExchangeType.BLOCK);
+ }
+
+ public static ExchangeEventInnerEntity newEmptyInstance() {
+ return new ExchangeEventInnerEntity(ExchangeType.EMPTY);
+ }
+
+ public static ExchangeEventInnerEntity newTransactionInstance(byte[] content) {
+ return new ExchangeEventInnerEntity(ExchangeType.TRANSACTION, content);
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventFactory.java
new file mode 100644
index 00000000..6d16b8c7
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventFactory.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.rabbitmq.nats.consensus.disruptor.ExchangeEventFactory
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 上午10:48
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.exchange;
+
+import com.jd.blockchain.consensus.event.EventEntity;
+import com.lmax.disruptor.EventFactory;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class ExchangeEventFactory implements EventFactory> {
+
+ public static final int BUFFER_SIZE = 256 * 1024;
+// public static final int BUFFER_SIZE = 8 * 1024;
+
+ @Override
+ public EventEntity newInstance() {
+ return new EventEntity<>();
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventInnerEntity.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventInnerEntity.java
new file mode 100644
index 00000000..752c1414
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventInnerEntity.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.sdk.ExchangeEventInnerEntity
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午4:04
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.exchange;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class ExchangeEventInnerEntity {
+
+ private ExchangeType type;
+
+ private byte[] content;
+
+ public ExchangeEventInnerEntity() {
+ }
+
+ public ExchangeEventInnerEntity(ExchangeType type) {
+ this.type = type;
+ }
+
+ public ExchangeEventInnerEntity(ExchangeType type, byte[] content) {
+ this.type = type;
+ this.content = content;
+ }
+
+
+
+ public ExchangeType getType() {
+ return type;
+ }
+
+ public void setType(ExchangeType type) {
+ this.type = type;
+ }
+
+ public byte[] getContent() {
+ return content;
+ }
+
+ public void setContent(byte[] content) {
+ this.content = content;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventProducer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventProducer.java
new file mode 100644
index 00000000..942fe167
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventProducer.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.rabbitmq.nats.consensus.disruptor.ExchangeEventProducer
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 上午10:50
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.exchange;
+
+import com.jd.blockchain.consensus.event.EventEntity;
+import com.jd.blockchain.consensus.event.EventProducer;
+import com.lmax.disruptor.RingBuffer;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class ExchangeEventProducer implements EventProducer {
+
+ private final RingBuffer> ringBuffer;
+
+ public ExchangeEventProducer(RingBuffer> ringBuffer) {
+ this.ringBuffer = ringBuffer;
+ }
+
+ @Override
+ public void publish(ExchangeEventInnerEntity entity) {
+ long sequence = ringBuffer.next();
+ try {
+ EventEntity event = ringBuffer.get(sequence);
+ event.setEntity(entity);
+ } finally {
+ this.ringBuffer.publish(sequence);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeType.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeType.java
new file mode 100644
index 00000000..1920d89e
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeType.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.sdk.nats.ExchangeType
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午3:34
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.exchange;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public enum ExchangeType {
+ BLOCK,
+ EMPTY,
+ TRANSACTION,
+ ;
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/MsgQueueConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/MsgQueueConfig.java
new file mode 100644
index 00000000..1d4419a7
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/MsgQueueConfig.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.factory.MsgQueueConfig
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 下午5:16
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.factory;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public final class MsgQueueConfig {
+
+ public static final String NATS_PREFIX = "nats";
+
+ public static final String RABBIT_PREFIX = "rabbit";
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/MsgQueueFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/MsgQueueFactory.java
new file mode 100644
index 00000000..db2b0c1a
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/MsgQueueFactory.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: MsgQueueFactory
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午10:13
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.factory;
+
+
+import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer;
+import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer;
+
+import static com.jd.blockchain.consensus.mq.factory.MsgQueueConfig.NATS_PREFIX;
+import static com.jd.blockchain.consensus.mq.factory.MsgQueueConfig.RABBIT_PREFIX;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class MsgQueueFactory {
+
+ public static MsgQueueProducer newProducer(String server, String topic) {
+ try {
+ if (server.startsWith(NATS_PREFIX)) {
+ return NatsFactory.newProducer(server, topic);
+ } else if (server.startsWith(RABBIT_PREFIX)) {
+ return RabbitFactory.newProducer(server, topic);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ }
+
+ public static MsgQueueConsumer newConsumer(String server, String topic) {
+ try {
+ if (server.startsWith(NATS_PREFIX)) {
+ return NatsFactory.newConsumer(server, topic);
+ } else if (server.startsWith(RABBIT_PREFIX)) {
+ return RabbitFactory.newConsumer(server, topic);
+ }
+ return null;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/NatsFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/NatsFactory.java
new file mode 100644
index 00000000..c5a4d8f0
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/NatsFactory.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: NatsFactory
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午10:15
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.factory;
+
+import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer;
+import com.jd.blockchain.consensus.mq.consumer.NatsConsumer;
+import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer;
+import com.jd.blockchain.consensus.mq.producer.NatsProducer;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class NatsFactory {
+
+ public static MsgQueueProducer newProducer(String server, String topic) throws Exception {
+ return new NatsProducer(server, topic);
+ }
+
+ public static MsgQueueConsumer newConsumer(String server, String topic) throws Exception {
+ return new NatsConsumer(server, topic);
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/RabbitFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/RabbitFactory.java
new file mode 100644
index 00000000..b66cf3b6
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/RabbitFactory.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.sdk.nats.RabbitFactory
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午10:15
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.factory;
+
+import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer;
+import com.jd.blockchain.consensus.mq.consumer.RabbitConsumer;
+import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer;
+import com.jd.blockchain.consensus.mq.producer.RabbitProducer;
+import com.rabbitmq.client.ConnectionFactory;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class RabbitFactory {
+
+ public static MsgQueueProducer newProducer(String server, String topic) throws Exception {
+ return new RabbitProducer(server, topic);
+ }
+
+ public static MsgQueueConsumer newConsumer(String server, String topic) throws Exception {
+ return new RabbitConsumer(server, topic);
+ }
+
+ public static ConnectionFactory initConnectionFactory(String server) {
+ ConnectionFactory factory = new ConnectionFactory();
+ // 解析server,生成host+port,默认格式:rabbit://localhost:5672
+ try {
+ String[] hostAndPort = server.split("//")[1].split(":");
+ if (hostAndPort == null || hostAndPort.length == 0) {
+ factory.setHost("localhost");
+ } else if (hostAndPort.length == 1) {
+ factory.setHost(hostAndPort[0]);
+ } else {
+ factory.setHost(hostAndPort[0]);
+ factory.setPort(Integer.parseInt(hostAndPort[1]));
+ }
+ } catch (Exception e) {
+ factory.setHost("localhost");
+ }
+ return factory;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/MsgQueueProducer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/MsgQueueProducer.java
new file mode 100644
index 00000000..91cee801
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/MsgQueueProducer.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: MsgQueueProducer
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午10:37
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.producer;
+
+import java.io.Closeable;
+import java.util.List;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public interface MsgQueueProducer extends Closeable {
+
+ void connect() throws Exception;
+
+ void publish(byte[] message) throws Exception;
+
+ void publishString(String message) throws Exception;
+
+ void publishStringList(List messages) throws Exception;
+
+ void publishStringArray(String[] messages) throws Exception;
+
+ void publishBytesArray(byte[][] message) throws Exception;
+
+ void publishBytesList(List messages) throws Exception;
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/NatsProducer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/NatsProducer.java
new file mode 100644
index 00000000..2f3b2357
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/NatsProducer.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: NatsProducer
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午10:39
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.producer;
+
+import io.nats.client.Connection;
+import io.nats.client.Nats;
+import io.nats.client.Options;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import com.jd.blockchain.utils.ConsoleUtils;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class NatsProducer implements MsgQueueProducer {
+
+ // 主要操作:发送MQ请求
+ private Connection nc;
+
+ private String server;
+
+ // 主题
+ private String topic;
+
+ public NatsProducer() {
+
+ }
+
+ public NatsProducer(String server, String topic) {
+ this.topic = topic;
+ this.server = server;
+ }
+
+ @Override
+ public void connect() throws Exception{
+ Options o = new Options.Builder().server(server).noReconnect().build();
+ this.nc = Nats.connect(o);
+ ConsoleUtils.info("[*] NatsProducer[%s, %s] connect success !!!", this.server, this.topic);
+ }
+
+ @Override
+ public void publish(byte[] message) {
+ nc.publish(topic, message);
+ }
+
+ @Override
+ public void publishString(String message) {
+ publish(message.getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Override
+ public void publishStringList(List messages) {
+ for (String message : messages) {
+ publishString(message);
+ }
+ }
+
+ @Override
+ public void publishStringArray(String[] messages) {
+ for (String message : messages) {
+ publishString(message);
+ }
+ }
+
+ @Override
+ public void publishBytesArray(byte[][] message) {
+ for (byte[] bytes : message) {
+ publish(bytes);
+ }
+ }
+
+ @Override
+ public void publishBytesList(List messages) {
+ for (byte[] message : messages) {
+ publish(message);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ nc.close();
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/RabbitProducer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/RabbitProducer.java
new file mode 100644
index 00000000..fcd6979b
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/RabbitProducer.java
@@ -0,0 +1,106 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.sdk.nats.RabbitProducer
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/5 下午10:39
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.producer;
+
+import com.jd.blockchain.consensus.mq.factory.RabbitFactory;
+import com.jd.blockchain.utils.ConsoleUtils;
+import com.rabbitmq.client.Channel;
+import com.rabbitmq.client.Connection;
+import com.rabbitmq.client.ConnectionFactory;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/5
+ * @since 1.0.0
+ */
+
+public class RabbitProducer implements MsgQueueProducer {
+
+ // 主要操作时发送JMQ请求
+ private Channel channel;
+
+ private Connection connection;
+
+ private String exchangeName;
+
+ private String server;
+
+ public RabbitProducer() {
+
+ }
+
+ public RabbitProducer(String server, String topic) throws Exception {
+ this.exchangeName = topic;
+ this.server = server;
+ }
+
+ @Override
+ public void connect() throws Exception {
+ ConnectionFactory factory = RabbitFactory.initConnectionFactory(server);
+ connection = factory.newConnection();
+ channel = connection.createChannel();
+ channel.exchangeDeclare(this.exchangeName, "fanout");
+ ConsoleUtils.info("[*] RabbitProducer[%s, %s] connect success !!!", this.server, this.exchangeName);
+ }
+
+ @Override
+ public void publish(byte[] message) throws Exception {
+ channel.basicPublish(this.exchangeName, "", null, message);
+ }
+
+ @Override
+ public void publishString(String message) throws Exception {
+ publish(message.getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Override
+ public void publishStringList(List messages) throws Exception {
+ for (String message : messages) {
+ publishString(message);
+ }
+ }
+
+ @Override
+ public void publishStringArray(String[] messages) throws Exception {
+ for (String message : messages) {
+ publishString(message);
+ }
+ }
+
+ @Override
+ public void publishBytesArray(byte[][] message) throws Exception {
+ for (byte[] bytes : message) {
+ publish(bytes);
+ }
+ }
+
+ @Override
+ public void publishBytesList(List messages) throws Exception {
+ for (byte[] message : messages) {
+ publish(message);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ channel.close();
+ connection.close();
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/DefaultMsgQueueMessageDispatcher.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/DefaultMsgQueueMessageDispatcher.java
new file mode 100644
index 00000000..63af3844
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/DefaultMsgQueueMessageDispatcher.java
@@ -0,0 +1,297 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.server.DefaultMsgQueueMessageDispatcher
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/13 上午11:05
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.server;
+
+import com.jd.blockchain.consensus.event.EventEntity;
+import com.jd.blockchain.consensus.event.EventProducer;
+import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer;
+import com.jd.blockchain.consensus.mq.exchange.ExchangeEntityFactory;
+import com.jd.blockchain.consensus.mq.exchange.ExchangeEventFactory;
+import com.jd.blockchain.consensus.mq.exchange.ExchangeEventInnerEntity;
+import com.jd.blockchain.consensus.mq.exchange.ExchangeEventProducer;
+import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer;
+import com.lmax.disruptor.BlockingWaitStrategy;
+import com.lmax.disruptor.EventHandler;
+import com.lmax.disruptor.RingBuffer;
+import com.lmax.disruptor.dsl.Disruptor;
+import com.lmax.disruptor.dsl.ProducerType;
+
+import java.io.IOException;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/13
+ * @since 1.0.0
+ */
+
+public class DefaultMsgQueueMessageDispatcher implements MsgQueueMessageDispatcher, EventHandler> {
+
+ private static final byte[] blockCommitBytes = new byte[]{0x00};
+
+ private final BlockingQueue dataQueue = new ArrayBlockingQueue<>(1024 * 16);
+
+ private final ExecutorService dataExecutor = Executors.newSingleThreadExecutor();
+
+ private final ScheduledThreadPoolExecutor timeHandleExecutor = new ScheduledThreadPoolExecutor(2);
+
+ private final AtomicLong blockIndex = new AtomicLong();
+
+ private long syncIndex = 0L;
+
+ private MsgQueueProducer txProducer;
+
+ private MsgQueueConsumer txConsumer;
+
+ private EventProducer eventProducer;
+
+ private EventHandler eventHandler;
+
+ private final int TX_SIZE_PER_BLOCK;
+
+ private final long MAX_DELAY_MILLISECONDS_PER_BLOCK;
+
+ private boolean isRunning;
+
+ private boolean isConnected;
+
+ public DefaultMsgQueueMessageDispatcher(int txSizePerBlock, long maxDelayMilliSecondsPerBlock) {
+ this.TX_SIZE_PER_BLOCK = txSizePerBlock;
+ this.MAX_DELAY_MILLISECONDS_PER_BLOCK = maxDelayMilliSecondsPerBlock;
+ }
+
+ public DefaultMsgQueueMessageDispatcher setTxProducer(MsgQueueProducer txProducer) {
+ this.txProducer = txProducer;
+ return this;
+ }
+
+ public DefaultMsgQueueMessageDispatcher setTxConsumer(MsgQueueConsumer txConsumer) {
+ this.txConsumer = txConsumer;
+ return this;
+ }
+
+ public DefaultMsgQueueMessageDispatcher setEventHandler(EventHandler eventHandler) {
+ this.eventHandler = eventHandler;
+ return this;
+ }
+
+ public void init() {
+ handleDisruptor(eventHandler);
+ }
+
+ private void handleDisruptor(EventHandler eventHandler) {
+ Disruptor> disruptor =
+ new Disruptor<>(new ExchangeEventFactory(),
+ ExchangeEventFactory.BUFFER_SIZE, r -> {
+ return new Thread(r);
+ }, ProducerType.SINGLE, new BlockingWaitStrategy());
+
+ disruptor.handleEventsWith(eventHandler);
+ disruptor.start();
+ RingBuffer> ringBuffer = disruptor.getRingBuffer();
+
+ this.eventProducer = new ExchangeEventProducer(ringBuffer);
+ }
+
+ public synchronized void connect() throws Exception {
+ if (!isConnected) {
+ txProducer.connect();
+ txConsumer.connect(this);
+ isConnected = true;
+ }
+ }
+
+ @Override
+ public synchronized void stop() throws Exception {
+ isRunning = false;
+ close();
+ }
+
+ @Override
+ public void run() {
+ this.isRunning = true;
+ try {
+ txConsumer.start();
+ } catch (Exception e) {
+
+ }
+// handleData();
+// listen();
+ }
+
+// private void listen() {
+// while (isRunning) {
+// try {
+// byte[] data = this.txConsumer.start();
+// dataQueue.put(data);
+// // 收到数据后由队列处理
+//// handleData(data);
+// } catch (Exception e) {
+// // 日志打印
+// ConsoleUtils.info("ERROR dispatcher start data exception {%s}", e.getMessage());
+// }
+// }
+// }
+
+// private void handleData() {
+// dataExecutor.execute(() -> {
+// byte[] data;
+// for (;;) {
+// try {
+// data = dataQueue.take();
+// if (data.length == 1) {
+// // 结块标识优先处理
+// syncIndex = 0L;
+// this.blockIndex.getAndIncrement();
+// eventProducer.publish(ExchangeEntityFactory.newBlockInstance());
+// } else {
+// if (syncIndex == 0) { // 收到第一个交易
+// // 需要判断是否需要进行定时任务
+// if (MAX_DELAY_MILLISECONDS_PER_BLOCK > 0) {
+// this.timeHandleExecutor.schedule(
+// timeBlockTask(this.blockIndex.get()),
+// MAX_DELAY_MILLISECONDS_PER_BLOCK, TimeUnit.MILLISECONDS);
+// }
+// }
+// syncIndex++;
+// eventProducer.publish(ExchangeEntityFactory.newTransactionInstance(data));
+// if (syncIndex == TX_SIZE_PER_BLOCK) {
+// syncIndex = 0L;
+// this.blockIndex.getAndIncrement();
+// eventProducer.publish(ExchangeEntityFactory.newBlockInstance());
+// }
+// }
+//
+// } catch (Exception e) {
+// e.printStackTrace();
+// }
+// }
+// });
+// for (;;) {
+// try {
+// final byte[] data = dataQueue.take();
+// dataExecutor.execute(() -> {
+// if (data.length == 1) {
+// // 结块标识优先处理
+// syncIndex = 0L;
+// this.blockIndex.getAndIncrement();
+// eventProducer.publish(ExchangeEntityFactory.newBlockInstance());
+// } else {
+// if (syncIndex == 0) { // 收到第一个交易
+// // 需要判断是否需要进行定时任务
+// if (MAX_DELAY_MILLISECONDS_PER_BLOCK > 0) {
+// this.timeHandleExecutor.schedule(
+// timeBlockTask(this.blockIndex.get()),
+// MAX_DELAY_MILLISECONDS_PER_BLOCK, TimeUnit.MILLISECONDS);
+// }
+// }
+// syncIndex++;
+// eventProducer.publish(ExchangeEntityFactory.newTransactionInstance(data));
+// if (syncIndex == TX_SIZE_PER_BLOCK) {
+// syncIndex = 0L;
+// this.blockIndex.getAndIncrement();
+// eventProducer.publish(ExchangeEntityFactory.newBlockInstance());
+// }
+// }
+// }
+// );
+// } catch (Exception e) {
+// e.printStackTrace();
+// }
+// }
+// }
+
+// private void handleData(final byte[] data) {
+// dataExecutor.execute(() -> {
+// try {
+// if (data.length == 1) {
+// // 结块标识优先处理
+// syncIndex = 0L;
+// this.blockIndex.getAndIncrement();
+// eventProducer.publish(ExchangeEntityFactory.newBlockInstance());
+// } else {
+// if (syncIndex == 0) { // 收到第一个交易
+// // 需要判断是否需要进行定时任务
+// if (MAX_DELAY_MILLISECONDS_PER_BLOCK > 0) {
+// this.timeHandleExecutor.schedule(
+// timeBlockTask(this.blockIndex.get()),
+// MAX_DELAY_MILLISECONDS_PER_BLOCK, TimeUnit.MILLISECONDS);
+// }
+// }
+// syncIndex++;
+// eventProducer.publish(ExchangeEntityFactory.newTransactionInstance(data));
+// if (syncIndex == TX_SIZE_PER_BLOCK) {
+// syncIndex = 0L;
+// this.blockIndex.getAndIncrement();
+// eventProducer.publish(ExchangeEntityFactory.newBlockInstance());
+// }
+// }
+// } catch (Exception e) {
+// // 记录日志
+// ConsoleUtils.info("ERROR TransactionDispatcher process queue data exception {%s}", e.getMessage());
+// }
+// });
+//
+// }
+
+ private Runnable timeBlockTask(final long currentBlockIndex) {
+ return () -> {
+ final boolean isEqualBlock = this.blockIndex.compareAndSet(
+ currentBlockIndex, currentBlockIndex + 1);
+ if (isEqualBlock) {
+ try {
+ txProducer.publish(blockCommitBytes);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ }
+
+ @Override
+ public void close() throws IOException {
+ this.txProducer.close();
+ this.txConsumer.close();
+ }
+
+ @Override
+ public void onEvent(EventEntity event, long sequence, boolean endOfBatch) throws Exception {
+ try {
+ byte[] data = event.getEntity();
+// System.out.printf("Thread [%s, $s] on event !!!\r\n",
+// Thread.currentThread().getId(), Thread.currentThread().getName());
+ if (data.length == 1) {
+ // 结块标识优先处理
+ syncIndex = 0L;
+ this.blockIndex.getAndIncrement();
+ eventProducer.publish(ExchangeEntityFactory.newBlockInstance());
+ } else {
+ if (syncIndex == 0) { // 收到第一个交易
+ // 需要判断是否需要进行定时任务
+ if (MAX_DELAY_MILLISECONDS_PER_BLOCK > 0) {
+ this.timeHandleExecutor.schedule(
+ timeBlockTask(this.blockIndex.get()),
+ MAX_DELAY_MILLISECONDS_PER_BLOCK, TimeUnit.MILLISECONDS);
+ }
+ }
+ syncIndex++;
+ eventProducer.publish(ExchangeEntityFactory.newTransactionInstance(data));
+ if (syncIndex == TX_SIZE_PER_BLOCK) {
+ syncIndex = 0L;
+ this.blockIndex.getAndIncrement();
+ eventProducer.publish(ExchangeEntityFactory.newBlockInstance());
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/ExtendMsgQueueMessageExecutor.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/ExtendMsgQueueMessageExecutor.java
new file mode 100644
index 00000000..9a73ece4
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/ExtendMsgQueueMessageExecutor.java
@@ -0,0 +1,126 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.server.DefaultMsgQueueMessageDispatcher
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/13 上午11:05
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.server;
+
+import com.jd.blockchain.consensus.event.EventEntity;
+import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer;
+import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer;
+import com.jd.blockchain.consensus.service.MessageHandle;
+import com.jd.blockchain.utils.concurrent.AsyncFuture;
+import com.lmax.disruptor.EventHandler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/13
+ * @since 1.0.0
+ */
+
+public class ExtendMsgQueueMessageExecutor implements MsgQueueMessageDispatcher, EventHandler> {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ExtendMsgQueueMessageExecutor.class);
+
+ private final ExecutorService dataExecutor = Executors.newSingleThreadExecutor();
+
+ private MsgQueueProducer msgProducer;
+
+ private MsgQueueConsumer msgConsumer;
+
+ private MessageHandle messageHandle;
+
+ private boolean isRunning;
+
+ private boolean isConnected;
+
+ public ExtendMsgQueueMessageExecutor setMsgProducer(MsgQueueProducer msgProducer) {
+ this.msgProducer = msgProducer;
+ return this;
+ }
+
+ public ExtendMsgQueueMessageExecutor setMsgConsumer(MsgQueueConsumer msgConsumer) {
+ this.msgConsumer = msgConsumer;
+ return this;
+ }
+
+ public ExtendMsgQueueMessageExecutor setMessageHandle(MessageHandle messageHandle) {
+ this.messageHandle = messageHandle;
+ return this;
+ }
+
+ @Override
+ public void init() {
+ // do nothing
+ }
+
+ public synchronized void connect() throws Exception {
+ if (!isConnected) {
+ msgProducer.connect();
+ msgConsumer.connect(this);
+ msgConsumer.start();
+ isConnected = true;
+ }
+ }
+
+ @Override
+ public synchronized void stop() throws Exception {
+ isRunning = false;
+ close();
+ }
+
+ @Override
+ public void run() {
+ this.isRunning = true;
+// this.msgConsumer.start();
+// listen();
+ }
+
+// private void listen() {
+// while (isRunning) {
+// try {
+// byte[] data = this.msgConsumer.start();
+// // 收到数据后由队列处理
+// handleData(data);
+// } catch (Exception e) {
+// // 日志打印
+// LOGGER.error("extend message handle exception {}", e.getMessage());
+// }
+// }
+// }
+
+ private void handleData(byte[] data) {
+ dataExecutor.execute(() -> {
+ try {
+ AsyncFuture result = messageHandle.processUnordered(data);
+ msgProducer.publish(result.get());
+ } catch (Exception e) {
+ LOGGER.error("process Unordered message exception {}", e.getMessage());
+ }
+ });
+ }
+
+ @Override
+ public void close() throws IOException {
+ isConnected = false;
+ this.msgProducer.close();
+ this.msgConsumer.close();
+ }
+
+ @Override
+ public void onEvent(EventEntity event, long sequence, boolean endOfBatch) throws Exception {
+ byte[] data = event.getEntity();
+ handleData(data);
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueConsensusManageService.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueConsensusManageService.java
new file mode 100644
index 00000000..d33c462e
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueConsensusManageService.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.server.MsgQueueConsensusManageService
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 下午1:46
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.server;
+
+import com.jd.blockchain.consensus.ClientIdentification;
+import com.jd.blockchain.consensus.ConsensusManageService;
+import com.jd.blockchain.consensus.ConsensusSecurityException;
+import com.jd.blockchain.consensus.mq.client.MsgQueueClientIdentification;
+import com.jd.blockchain.consensus.mq.config.MsgQueueClientIncomingConfig;
+import com.jd.blockchain.consensus.mq.config.MsgQueueConsensusConfig;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueClientIncomingSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings;
+import com.jd.blockchain.crypto.CryptoUtils;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.asymmetric.SignatureFunction;
+
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueConsensusManageService implements ConsensusManageService {
+
+ private MsgQueueConsensusSettings consensusSettings;
+
+ public MsgQueueConsensusManageService setConsensusSettings(MsgQueueConsensusSettings consensusSettings) {
+ this.consensusSettings = consensusSettings;
+ return this;
+ }
+
+ @Override
+ public MsgQueueClientIncomingSettings authClientIncoming(ClientIdentification authId) throws ConsensusSecurityException {
+ boolean isLegal = isLegal(authId);
+ if (isLegal) {
+ MsgQueueClientIncomingSettings mqcis = new MsgQueueClientIncomingConfig()
+ .setPubKey(authId.getPubKey())
+ .setClientId(clientId(authId.getIdentityInfo()))
+ .setConsensusSettings(this.consensusSettings)
+ ;
+ return mqcis;
+ }
+ return null;
+ }
+
+ private int clientId(byte[] identityInfo) {
+ // todo
+
+ return 0;
+ }
+
+ public boolean isLegal(ClientIdentification authId) {
+ boolean isLegal = false;
+ PubKey pubKey = authId.getPubKey();
+ byte[] identityInfo = authId.getIdentityInfo();
+ byte[] address = pubKey.toBytes(); // 使用公钥地址作为认证信息
+ if (Arrays.equals(address, identityInfo)) {
+ SignatureFunction signatureFunction = CryptoUtils.sign(pubKey.getAlgorithm());
+ isLegal = signatureFunction.verify(authId.getSignature(), pubKey, identityInfo);
+ }
+ return isLegal;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueMessageDispatcher.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueMessageDispatcher.java
new file mode 100644
index 00000000..a3b6e286
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueMessageDispatcher.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.server.MsgQueueMessageDispatcher
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 上午11:30
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.server;
+
+
+import java.io.Closeable;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public interface MsgQueueMessageDispatcher extends Runnable, Closeable {
+
+ void init();
+
+ void connect() throws Exception;
+
+ void stop() throws Exception;
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueMessageExecutor.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueMessageExecutor.java
new file mode 100644
index 00000000..36454696
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueMessageExecutor.java
@@ -0,0 +1,182 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.server.MsgQueueMessageExecutor
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/13 下午2:10
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.server;
+
+import com.jd.blockchain.consensus.event.EventEntity;
+import com.jd.blockchain.consensus.mq.event.MessageEvent;
+import com.jd.blockchain.consensus.mq.event.TxBlockedEvent;
+import com.jd.blockchain.consensus.mq.exchange.ExchangeEventInnerEntity;
+import com.jd.blockchain.consensus.mq.exchange.ExchangeType;
+import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer;
+import com.jd.blockchain.consensus.mq.util.MessageConvertUtil;
+import com.jd.blockchain.consensus.service.MessageHandle;
+import com.jd.blockchain.consensus.service.StateMachineReplicate;
+import com.jd.blockchain.ledger.TransactionState;
+import com.jd.blockchain.utils.concurrent.AsyncFuture;
+import com.lmax.disruptor.EventHandler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/13
+ * @since 1.0.0
+ */
+
+public class MsgQueueMessageExecutor implements EventHandler> {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MsgQueueMessageExecutor.class);
+
+ // todo 暂不处理队列溢出导致的OOM
+ private final ExecutorService blockEventExecutor = Executors.newFixedThreadPool(10);
+
+ private MsgQueueProducer blProducer;
+
+ private List exchangeEvents = new ArrayList<>();
+
+ private String realmName;
+
+ private MessageHandle messageHandle;
+
+ private final AtomicInteger messageId = new AtomicInteger();
+
+ private int txSizePerBlock = 1000;
+
+ private StateMachineReplicate stateMachineReplicator;
+
+ public MsgQueueMessageExecutor setRealmName(String realmName) {
+ this.realmName = realmName;
+ return this;
+ }
+
+ public MsgQueueMessageExecutor setBlProducer(MsgQueueProducer blProducer) {
+ this.blProducer = blProducer;
+ return this;
+ }
+
+ public MsgQueueMessageExecutor setTxSizePerBlock(int txSizePerBlock) {
+ this.txSizePerBlock = txSizePerBlock;
+ return this;
+ }
+
+ public MsgQueueMessageExecutor setMessageHandle(MessageHandle messageHandle) {
+ this.messageHandle = messageHandle;
+ return this;
+ }
+
+ public MsgQueueMessageExecutor setStateMachineReplicator(StateMachineReplicate stateMachineReplicator) {
+ this.stateMachineReplicator = stateMachineReplicator;
+ return this;
+ }
+
+ public MsgQueueMessageExecutor init() {
+ try {
+ long latestStateId = stateMachineReplicator.getLatestStateID(realmName);
+ // 设置基础消息ID
+ messageId.set(((int)latestStateId + 1) * txSizePerBlock);
+ blProducer.connect();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return this;
+ }
+
+ @Override
+ public void onEvent(EventEntity event, long sequence, boolean endOfBatch) throws Exception {
+ ExchangeEventInnerEntity entity = event.getEntity();
+ if (entity != null) {
+ if (entity.getType() == ExchangeType.BLOCK || entity.getType() == ExchangeType.EMPTY) {
+ if (!exchangeEvents.isEmpty()) {
+ process(exchangeEvents);
+ exchangeEvents.clear();
+ }
+ } else {
+ byte[] bytes = event.getEntity().getContent();
+ String key = bytes2Key(bytes);
+ exchangeEvents.add(new MessageEvent(key, bytes));
+ }
+ }
+ }
+
+ private void process(List messageEvents) {
+ if (messageEvents != null && !messageEvents.isEmpty()) {
+ try {
+ Map> txResponseMap = execute(messageEvents);
+ if (txResponseMap != null && !txResponseMap.isEmpty()) {
+// byte[] asyncFuture;
+ for (Map.Entry> entry : txResponseMap.entrySet()) {
+ final String txKey = entry.getKey();
+ final AsyncFuture asyncFuture = entry.getValue();
+// asyncFuture = entry.getValue().get();
+
+ blockEventExecutor.execute(() -> {
+ TxBlockedEvent txBlockedEvent = new TxBlockedEvent(txKey,
+ MessageConvertUtil.base64Encode(asyncFuture.get()));
+ byte[] serializeBytes = MessageConvertUtil.serializeTxBlockedEvent(txBlockedEvent);
+ // 通过消息队列发送该消息
+ try {
+ this.blProducer.publish(serializeBytes);
+ } catch (Exception e) {
+ LOGGER.error("publish block event message exception {}", e.getMessage());
+ }
+ });
+ }
+ }
+ } catch (Exception e) {
+ // 打印日志
+ LOGGER.error("process message exception {}", e.getMessage());
+ }
+ }
+ }
+
+ private Map> execute(List messageEvents) {
+// System.out.printf("Thread[%s %s] execute messageEvents !!! \r\n",
+// Thread.currentThread().getId(), Thread.currentThread().getName());
+ Map> asyncFutureMap = new HashMap<>();
+ // 使用MessageHandle处理
+// long startTime = System.currentTimeMillis();
+// int txSize = messageEvents.size();
+ String batchId = messageHandle.beginBatch(realmName);
+ try {
+ for (MessageEvent messageEvent : messageEvents) {
+ String txKey = messageEvent.getMessageKey();
+ byte[] txContent = messageEvent.getMessage();
+ AsyncFuture asyncFuture = messageHandle.processOrdered(messageId.getAndIncrement(), txContent, realmName, batchId);
+ asyncFutureMap.put(txKey, asyncFuture);
+ }
+ messageHandle.completeBatch(realmName, batchId);
+ messageHandle.commitBatch(realmName, batchId);
+// long totalTime = System.currentTimeMillis() - startTime;
+// String content = String.format("batch[%s] process, time = {%s}ms, TPS = %.2f \r\n",
+// batchId, totalTime, txSize * 1000.0D / totalTime);
+// System.out.println(content);
+// logQueue.put(content);
+ // 提交之后需要获取对应的结果
+ } catch (Exception e) {
+ // todo 需要处理应答码 404
+ messageHandle.rollbackBatch(realmName, batchId, TransactionState.CONSENSUS_ERROR.CODE);
+ }
+ return asyncFutureMap;
+ }
+
+
+ private String bytes2Key(byte[] bytes) {
+ return MessageConvertUtil.messageKey(bytes);
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueNodeServer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueNodeServer.java
new file mode 100644
index 00000000..3f1f0b82
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueNodeServer.java
@@ -0,0 +1,196 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.server.MsgQueueNodeServer
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/13 上午11:20
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.server;
+
+import com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider;
+import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer;
+import com.jd.blockchain.consensus.mq.factory.MsgQueueFactory;
+import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueServerSettings;
+import com.jd.blockchain.consensus.service.MessageHandle;
+import com.jd.blockchain.consensus.service.NodeServer;
+import com.jd.blockchain.consensus.service.StateMachineReplicate;
+
+import java.util.concurrent.Executors;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/13
+ * @since 1.0.0
+ */
+
+public class MsgQueueNodeServer implements NodeServer {
+
+ private DefaultMsgQueueMessageDispatcher dispatcher;
+
+ private ExtendMsgQueueMessageExecutor extendExecutor;
+
+ private MessageHandle messageHandle;
+
+ private StateMachineReplicate stateMachineReplicator;
+
+ private MsgQueueMessageExecutor messageExecutor;
+
+ private MsgQueueNetworkSettings networkSettings;
+
+ private MsgQueueConsensusManageService manageService;
+
+ private int txSizePerBlock = 1000;
+
+ private long maxDelayMilliSecondsPerBlock = 1000;
+
+ private MsgQueueServerSettings serverSettings;
+
+ private boolean isRunning;
+
+ public MsgQueueNodeServer setMessageHandle(MessageHandle messageHandle) {
+ this.messageHandle = messageHandle;
+ return this;
+ }
+
+ public MsgQueueNodeServer setStateMachineReplicator(StateMachineReplicate stateMachineReplicator) {
+ this.stateMachineReplicator = stateMachineReplicator;
+ return this;
+ }
+
+ public MsgQueueNodeServer setTxSizePerBlock(int txSizePerBlock) {
+ this.txSizePerBlock = txSizePerBlock;
+ return this;
+ }
+
+ public MsgQueueNodeServer setMaxDelayMilliSecondsPerBlock(long maxDelayMilliSecondsPerBlock) {
+ this.maxDelayMilliSecondsPerBlock = maxDelayMilliSecondsPerBlock;
+ return this;
+ }
+
+ public MsgQueueNodeServer setMsgQueueNetworkSettings(MsgQueueNetworkSettings networkSettings) {
+ this.networkSettings = networkSettings;
+ return this;
+ }
+
+ public MsgQueueNodeServer setServerSettings(MsgQueueServerSettings serverSettings) {
+ this.serverSettings = serverSettings;
+ this.manageService = new MsgQueueConsensusManageService()
+ .setConsensusSettings(serverSettings.getConsensusSettings());
+ return this;
+ }
+
+ public MsgQueueNodeServer init() {
+ String realmName = this.serverSettings.getRealmName();
+ MsgQueueBlockSettings blockSettings = this.serverSettings.getBlockSettings();
+ MsgQueueConsensusSettings consensusSettings = this.serverSettings.getConsensusSettings();
+
+ this.setTxSizePerBlock(blockSettings.getTxSizePerBlock())
+ .setMaxDelayMilliSecondsPerBlock(blockSettings.getMaxDelayMilliSecondsPerBlock())
+ .setMsgQueueNetworkSettings(consensusSettings.getNetworkSettings())
+ ;
+
+ String server = networkSettings.getServer(),
+ txTopic = networkSettings.getTxTopic(),
+ blTopic = networkSettings.getBlTopic(),
+ msgTopic = networkSettings.getMsgTopic();
+
+ MsgQueueProducer blProducer = MsgQueueFactory.newProducer(server, blTopic),
+ txProducer = MsgQueueFactory.newProducer(server, txTopic),
+ msgProducer = MsgQueueFactory.newProducer(server, msgTopic);
+
+ MsgQueueConsumer txConsumer = MsgQueueFactory.newConsumer(server, txTopic),
+ msgConsumer = MsgQueueFactory.newConsumer(server, msgTopic);
+
+ initMessageExecutor(blProducer, realmName);
+
+ initDispatcher(txProducer, txConsumer);
+
+ initExtendExecutor(msgProducer, msgConsumer);
+
+ return this;
+ }
+
+ @Override
+ public String getProviderName() {
+ return MsgQueueConsensusProvider.NAME;
+ }
+
+ @Override
+ public MsgQueueConsensusManageService getManageService() {
+ return this.manageService;
+ }
+
+ @Override
+ public MsgQueueServerSettings getSettings() {
+ return serverSettings;
+ }
+
+ @Override
+ public boolean isRunning() {
+ return isRunning;
+ }
+
+ @Override
+ public synchronized void start() {
+ if (!isRunning) {
+ try {
+ dispatcher.connect();
+ Executors.newSingleThreadExecutor().execute(dispatcher);
+ extendExecutor.connect();
+ Executors.newSingleThreadExecutor().execute(extendExecutor);
+ isRunning = true;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void stop() {
+ if (isRunning) {
+ try {
+ dispatcher.stop();
+ extendExecutor.stop();
+ isRunning = false;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private void initMessageExecutor(MsgQueueProducer blProducer, final String realmName) {
+ messageExecutor = new MsgQueueMessageExecutor()
+ .setRealmName(realmName)
+ .setMessageHandle(messageHandle)
+ .setBlProducer(blProducer)
+ .setStateMachineReplicator(stateMachineReplicator)
+ .setTxSizePerBlock(txSizePerBlock)
+ .init()
+ ;
+ }
+
+ private void initDispatcher(MsgQueueProducer txProducer, MsgQueueConsumer txConsumer) {
+ dispatcher = new DefaultMsgQueueMessageDispatcher(txSizePerBlock, maxDelayMilliSecondsPerBlock)
+ .setTxProducer(txProducer)
+ .setTxConsumer(txConsumer)
+ .setEventHandler(messageExecutor)
+ ;
+ dispatcher.init();
+ }
+
+
+ private void initExtendExecutor(MsgQueueProducer msgProducer, MsgQueueConsumer msgConsumer) {
+ extendExecutor = new ExtendMsgQueueMessageExecutor()
+ .setMessageHandle(messageHandle)
+ .setMsgConsumer(msgConsumer)
+ .setMsgProducer(msgProducer)
+ ;
+ extendExecutor.init();
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueNodeServerFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueNodeServerFactory.java
new file mode 100644
index 00000000..c8d8f337
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueNodeServerFactory.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.server.MsgQueueNodeServerFactory
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 上午11:30
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.server;
+
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.consensus.mq.config.MsgQueueNodeConfig;
+import com.jd.blockchain.consensus.mq.config.MsgQueueServerConfig;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings;
+import com.jd.blockchain.consensus.mq.settings.MsgQueueServerSettings;
+import com.jd.blockchain.consensus.service.*;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+
+public class MsgQueueNodeServerFactory implements NodeServerFactory {
+
+ @Override
+ public MsgQueueServerSettings buildServerSettings(String realmName, ConsensusSettings consensusSetting, String currentNodeAddress) {
+
+ if (!(consensusSetting instanceof MsgQueueConsensusSettings)) {
+ throw new IllegalArgumentException("ConsensusSettings data isn't supported! Accept MsgQueueConsensusSettings only!");
+ }
+
+ MsgQueueNodeSettings nodeSettings = new MsgQueueNodeConfig().setAddress(currentNodeAddress);
+
+ MsgQueueServerSettings serverSettings = new MsgQueueServerConfig()
+ .setRealmName(realmName)
+ .setNodeSettings(nodeSettings)
+ .setConsensusSettings((MsgQueueConsensusSettings) consensusSetting)
+ ;
+ return serverSettings;
+
+
+ }
+
+ @Override
+ public MsgQueueNodeServer setupServer(ServerSettings serverSettings, MessageHandle messageHandler, StateMachineReplicate stateMachineReplicator) {
+ if (!(serverSettings instanceof MsgQueueServerSettings)) {
+ throw new IllegalArgumentException("ServerSettings data isn't supported! Accept MsgQueueServerSettings only!");
+ }
+
+ MsgQueueNodeServer nodeServer = new MsgQueueNodeServer()
+ .setServerSettings((MsgQueueServerSettings) serverSettings)
+ .setMessageHandle(messageHandler)
+ .setStateMachineReplicator(stateMachineReplicator)
+ .init()
+ ;
+ return nodeServer;
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueBlockSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueBlockSettings.java
new file mode 100644
index 00000000..95ba2000
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueBlockSettings.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueBlockSettings
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/13 下午4:28
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.settings;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/13
+ * @since 1.0.0
+ */
+@DataContract(code = TypeCodes.CONSENSUS_MSGQUEUE_BLOCK_SETTINGS)
+public interface MsgQueueBlockSettings {
+
+ @DataField(order = 0, primitiveType = ValueType.INT32)
+ int getTxSizePerBlock();
+
+ @DataField(order = 1, primitiveType = ValueType.INT64)
+ long getMaxDelayMilliSecondsPerBlock();
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueClientIncomingSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueClientIncomingSettings.java
new file mode 100644
index 00000000..1eeffa91
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueClientIncomingSettings.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueClientIncomingSettings
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/13 下午4:35
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.settings;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.consensus.ClientIncomingSettings;
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/13
+ * @since 1.0.0
+ */
+@DataContract(code = TypeCodes.CONSENSUS_MSGQUEUE_CLI_INCOMING_SETTINGS)
+public interface MsgQueueClientIncomingSettings extends ClientIncomingSettings {
+
+ @DataField(order = 1, primitiveType=ValueType.BYTES)
+ PubKey getPubKey();
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueClientSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueClientSettings.java
new file mode 100644
index 00000000..95f24516
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueClientSettings.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueClientSettings
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/13 下午4:30
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.settings;
+
+import com.jd.blockchain.consensus.client.ClientSettings;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/13
+ * @since 1.0.0
+ */
+
+public interface MsgQueueClientSettings extends ClientSettings {
+
+ MsgQueueNetworkSettings getMsgQueueNetworkSettings();
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueConsensusSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueConsensusSettings.java
new file mode 100644
index 00000000..d24c5422
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueConsensusSettings.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueConsensusSettings
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/13 下午4:37
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.settings;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.consensus.ConsensusSettings;
+import com.jd.blockchain.consensus.mq.config.MsgQueueBlockConfig;
+import com.jd.blockchain.utils.Property;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/13
+ * @since 1.0.0
+ */
+@DataContract(code = TypeCodes.CONSENSUS_MSGQUEUE_SETTINGS)
+public interface MsgQueueConsensusSettings extends ConsensusSettings {
+
+ @DataField(order = 0, refContract = true)
+ MsgQueueNetworkSettings getNetworkSettings();
+
+ @DataField(order = 1, refContract = true)
+ MsgQueueBlockSettings getBlockSettings();
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueNetworkSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueNetworkSettings.java
new file mode 100644
index 00000000..d512c32b
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueNetworkSettings.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.mq.config.MsgQueueNetworkSettings
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/12 上午11:43
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.settings;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.utils.ValueType;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/12
+ * @since 1.0.0
+ */
+@DataContract(code = TypeCodes.CONSENSUS_MSGQUEUE_NETWORK_SETTINGS)
+public interface MsgQueueNetworkSettings {
+
+ @DataField(order = 0, primitiveType = ValueType.TEXT)
+ String getServer();
+
+ @DataField(order = 1, primitiveType = ValueType.TEXT)
+ String getTxTopic();
+
+ @DataField(order = 2, primitiveType = ValueType.TEXT)
+ String getBlTopic();
+
+ @DataField(order = 3, primitiveType = ValueType.TEXT)
+ String getMsgTopic();
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueNodeSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueNodeSettings.java
new file mode 100644
index 00000000..784a1708
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueNodeSettings.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/13 下午4:50
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.settings;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.consensus.NodeSettings;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/13
+ * @since 1.0.0
+ */
+
+@DataContract(code=TypeCodes.CONSENSUS_MSGQUEUE_NODE_SETTINGS)
+public interface MsgQueueNodeSettings extends NodeSettings {
+
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueServerSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueServerSettings.java
new file mode 100644
index 00000000..04ea74c2
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueServerSettings.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueServerSettings
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/12/13 下午4:39
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.settings;
+
+import com.jd.blockchain.consensus.service.ServerSettings;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/12/13
+ * @since 1.0.0
+ */
+
+public interface MsgQueueServerSettings extends ServerSettings {
+
+ MsgQueueBlockSettings getBlockSettings();
+
+ MsgQueueConsensusSettings getConsensusSettings();
+}
\ No newline at end of file
diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/util/MessageConvertUtil.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/util/MessageConvertUtil.java
new file mode 100644
index 00000000..c0b032a4
--- /dev/null
+++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/util/MessageConvertUtil.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.sdk.mq.event.MessageConvertUtil
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2018/11/21 下午7:28
+ * Description:
+ */
+package com.jd.blockchain.consensus.mq.util;
+
+import com.alibaba.fastjson.JSON;
+import com.jd.blockchain.consensus.mq.event.BlockEvent;
+import com.jd.blockchain.consensus.mq.event.TxBlockedEvent;
+import com.jd.blockchain.utils.security.ShaUtils;
+
+import org.springframework.util.Base64Utils;
+
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2018/11/21
+ * @since 1.0.0
+ */
+
+public class MessageConvertUtil {
+
+ public static final String defaultCharsetName = "UTF-8";
+
+ public static String base64Encode(byte[] src) {
+ return Base64Utils.encodeToString(src);
+ }
+
+ public static byte[] base64Decode(String src) {
+ return Base64Utils.decodeFromString(src);
+ }
+
+ public static String messageKey(byte[] src) {
+ return base64Encode(ShaUtils.hash_256(src));
+ }
+
+ public static BlockEvent convertBytes2BlockEvent(byte[] serializeBytes) {
+ String text;
+ try{
+ text = new String(serializeBytes, defaultCharsetName);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return convertString2BlockEvent(text);
+ }
+
+ public static BlockEvent convertString2BlockEvent(String serializeString) {
+ return JSON.parseObject(serializeString, BlockEvent.class);
+ }
+
+ public static TxBlockedEvent convertBytes2TxBlockedEvent(byte[] serializeBytes) {
+ String text;
+ try{
+ text = new String(serializeBytes, defaultCharsetName);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return convertString2TxBlockedEvent(text);
+ }
+
+ public static TxBlockedEvent convertString2TxBlockedEvent(String serializeString) {
+ return JSON.parseObject(serializeString, TxBlockedEvent.class);
+ }
+
+ public static byte[] serializeBlockEvent(BlockEvent blockEvent) {
+ String serializeString = serializeEvent(blockEvent);
+ byte[] serializeBytes;
+ try {
+ serializeBytes = serializeString.getBytes(defaultCharsetName);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return serializeBytes;
+ }
+
+ public static byte[] serializeTxBlockedEvent(TxBlockedEvent txBlockedEvent) {
+ String serializeString = JSON.toJSONString(txBlockedEvent);
+ byte[] serializeBytes;
+ try {
+ serializeBytes = serializeString.getBytes(defaultCharsetName);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return serializeBytes;
+ }
+
+ public static String serializeEvent(BlockEvent blockEvent) {
+ return JSON.toJSONString(blockEvent);
+ }
+}
\ No newline at end of file
diff --git a/source/consensus/pom.xml b/source/consensus/pom.xml
new file mode 100644
index 00000000..c7b8c41e
--- /dev/null
+++ b/source/consensus/pom.xml
@@ -0,0 +1,18 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ jdchain-root
+ 0.8.2.RELEASE
+
+ consensus
+ pom
+
+
+ consensus-framework
+ consensus-bftsmart
+ consensus-mq
+
+
\ No newline at end of file
diff --git a/source/contract/README.MD b/source/contract/README.MD
new file mode 100644
index 00000000..15b32c0d
--- /dev/null
+++ b/source/contract/README.MD
@@ -0,0 +1,22 @@
+合约相关说明
+
+1.编译合约入口:ContractCompilerCmdTest.java;
+2.账本调用合约测试入口:ContractEventSendOperationHandleTest.java;
+
+使用ContractEventSendOperationHandleTest.java进行单元测试,注意事项:
+1.设置合约使用PUB_CLASS_PATH、CORE_CLASS_PATH位置(sys-contract.properties);
+PUB包在根目录中的contract-libs文件夹;core包需要编译来生成。具体如下:
+1)进入contract-jar模块,执行maven命令:mvn clean assembly:assembly
+2)生成的core包位于模块的target中的contract-jar-xxx所在的coreLib中;
+3)将此coreLib目录作为CORE_CLASS_PATH指向的目录。
+2.编译生成合约压缩包,即执行:ContractCompilerCmdTest.java对应的mainTestOk();
+在编译之前,修改sys-contract.properties文件的变量CONTRACT_FROM_PATH(合约源文件位置)、CONTRACT_SAVE_TO_PATH(合约保存位置)。
+3.在合约保存位置中可看到生成的压缩包:xxx.contract;然后执行 ContractEventSendOperationHandleTest.java测试用例test1()即可。
+
+20180910版本改造
+1.在contract-jar中添加了mvn assembly处理逻辑,将合约用到的lib包全部放置其target/xxx/pubLib文件夹中,执行:mvn clean assembly:assembly
+2.修改了sys-contract.properties文件,新增了CONTRACT_CLASS_LIBS参数,将CONTRACT_CLASS_PATH专用于存储路径,CONTRACT_CLASS_LIBS来存放所有的jar包;
+3.删减了contract-libs文件夹中需要动态生成的jar;
+
+
+
diff --git a/source/contract/contract-compile/pom.xml b/source/contract/contract-compile/pom.xml
new file mode 100644
index 00000000..e6c67b88
--- /dev/null
+++ b/source/contract/contract-compile/pom.xml
@@ -0,0 +1,114 @@
+
+
+
+
+ contract
+ com.jd.blockchain
+ 0.8.3.RELEASE
+
+ 4.0.0
+
+ contract-compile
+
+
+
+ com.jd.blockchain
+ contract-model
+ ${project.version}
+ provided
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.8
+ 1.8
+ UTF-8
+ false
+ true
+ false
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.5
+
+
+
+
+ true
+
+
+
+
+ maven-assembly-plugin
+
+ contract
+ false
+
+
+ com.jd.blockchain.contract.AssetContract2
+
+
+
+ jar-with-dependencies
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+ com.jd.blockchain
+ contract-maven-plugin
+ ${project.version}
+
+
+
+
+
+
+ make-assembly
+ process-sources
+
+ checkImports
+
+
+
+
+
+ maven-jar-plugin
+ 3.0.2
+
+
+ default-jar
+ none
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
diff --git a/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract1.java b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract1.java
new file mode 100644
index 00000000..8fae2439
--- /dev/null
+++ b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract1.java
@@ -0,0 +1,217 @@
+package com.jd.blockchain.contract;
+
+import com.jd.blockchain.contract.model.*;
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.*;
+import com.jd.blockchain.utils.BaseConstant;
+import com.jd.blockchain.utils.codec.Base58Utils;
+import com.jd.blockchain.utils.io.ByteArray;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * mock the smart contract;
+ */
+@Contract
+public class AssetContract1 implements EventProcessingAwire {
+ // private static final Logger LOGGER = LoggerFactory.getLogger(AssetContract1.class);
+ // the address of asset manager;
+// private static String ASSET_ADDRESS = "2njZBNbFQcmKd385DxVejwSjy4driRzf9Pk";
+ private static String ASSET_ADDRESS = "";
+ //account address;
+ private static final String ACCOUNT_ADDRESS = "accountAddress";
+ String contractAddress = "2njZBNbFQcmKd385DxVejwSjy4driRzf9Pk";
+ String userPubKeyVal = "this is user's pubKey";
+
+ // the key of save asset;
+ private static final String KEY_TOTAL = "TOTAL";
+
+ // contractEvent context;
+ private ContractEventContext eventContext;
+ private Object eventContextObj;
+ private byte[] eventContextBytes;
+
+ @Override
+ public void beforeEvent(ContractEventContext contractEventContext) {
+ eventContext = contractEventContext;
+ System.out.println("in beforeEvent(),event is: "+contractEventContext.getEvent());
+ }
+
+
+ @Override
+ public void postEvent(ContractEventContext eventContext, ContractException error) {
+ this.eventContext = null;
+ }
+
+ @Override
+ public void postEvent(ContractException error) {
+ this.eventContextBytes = null;
+ }
+
+ @Override
+ public void postEvent() {
+ this.eventContextBytes = null;
+ System.out.println("postEvent(),over.");
+ }
+
+ /**
+ * issue-asset;
+ * @param contractEventContext
+ * @throws Exception
+ */
+ @ContractEvent(name = "issue-asset")
+ public void issue(ContractEventContext contractEventContext) throws Exception {
+ // String strArgs = (String)BytesUtils.getObjectFromBytes(args_);
+ byte [] args_ = contractEventContext.getArgs();
+ if(args_ == null){
+ return;
+ }
+ String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM);
+ long amount = Long.parseLong(args[0]);
+ // 新发行的资产的持有账户;
+ String assetHolderAddress = args[1];
+ String ASSET_ADDRESS = args[2];
+ String previousBlockHash = args[3];
+ String userAddress = args[4];
+ String contractAddress = args[5];
+ String txHash = args[6];
+ String pubKeyVal = args[7];
+
+// checkAllOwnersAgreementPermission();
+
+// long amount = BytesUtils.toLong(args[0]);
+
+ if (amount < 0) {
+ throw new ContractException("The amount is negative!");
+ }
+ if (amount == 0) {
+ return;
+ }
+
+// BlockchainAccount holderAccount = eventContext.getLedger().getAccount(currentLedgerHash(), assetHolderAddress);
+// if (holderAccount == null) {
+// throw new ContractError("The holder is not exist!");
+// }
+ HashDigest hashDigest = eventContext.getCurrentLedgerHash();
+
+// eventContext.getLedger().dataAccount(ACCOUNT_ADDRESS).set(KEY_TOTAL,"total new dataAccount".getBytes(),2);
+// KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(hashDigest, ASSET_ADDRESS, KEY_TOTAL,assetHolderAddress);
+// assert ByteArray.toHex("total new dataAccount".getBytes()).equals(kvEntries[0].getValue())
+// && ByteArray.toHex("abc new dataAccount".getBytes()).equals(kvEntries[1].getValue()) :
+// "getDataEntries() test,expect!=actual;";
+
+ KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(hashDigest, ASSET_ADDRESS,
+ KEY_TOTAL,assetHolderAddress,"ledgerHash"); //,"latestBlockHash"
+
+ assert ByteArray.toHex("total value,dataAccount".getBytes()).equals(kvEntries[0].getValue())
+ && ByteArray.toHex("abc value,dataAccount".getBytes()).equals(kvEntries[1].getValue()) :
+ "getDataEntries() test,expect=actual;";
+
+ //get the latest block;
+ LedgerBlock ledgerBlock = eventContext.getLedger().getBlock(hashDigest,
+ eventContext.getLedger().getLedger(hashDigest).getLatestBlockHeight());
+
+
+// assert "zhaogw".equals(new String(ledgerBlock.getLedgerHash().getRawDigest())) &&
+// "lisi".equals(new String(ledgerBlock.getPreviousHash().getRawDigest())) :
+// "getBlock(hash,long) test,expect!=actual;";
+ assert ByteArray.toHex(eventContext.getCurrentLedgerHash().getRawDigest()).equals(kvEntries[2].getValue()) &&
+ ledgerBlock.getPreviousHash().toBase58().equals(previousBlockHash) :
+ "getPreviousHash() test,expect!=acutal;";
+
+ //模拟:根据hash来获得区块;
+ LedgerBlock ledgerBlock1 = eventContext.getLedger().getBlock(hashDigest,ledgerBlock.getHash());
+
+ assert eventContext.getLedger().getTransactionCount(hashDigest,1) == 2 :
+ "getTransactionCount(),expect!=acutal";
+
+// assert "zhaogw".equals(new String(ledgerBlock1.getLedgerHash().getRawDigest())) &&
+// "lisi".equals(new String(ledgerBlock1.getPreviousHash().getRawDigest())) :
+// "getBlock(hash,blockHash) test,expect!=acutal;";
+ assert ByteArray.toHex(eventContext.getCurrentLedgerHash().getRawDigest()).equals(kvEntries[2].getValue()) &&
+ ledgerBlock1.getPreviousHash().toBase58().equals(previousBlockHash) :
+ "getBlock(hash,blockHash) test,expect!=acutal;";
+
+ assert ASSET_ADDRESS.equals(eventContext.getLedger().getDataAccount(hashDigest,ASSET_ADDRESS).getAddress()) :
+ "getDataAccount(hash,address), expect!=acutal";
+
+ PubKey pubKey = new PubKey(CryptoAlgorithm.ED25519, pubKeyVal.getBytes());
+ BlockchainIdentity contractID = new BlockchainIdentityData(pubKey);
+// assert contractID == contractEventContext.getLedger().dataAccounts().register(contractID).getAccountID() :
+// "dataAccounts(),expect!=acutal";
+ contractEventContext.getLedger().dataAccounts().register(contractID);
+ contractEventContext.getLedger().dataAccount(contractID.getAddress()).
+ set(KEY_TOTAL,"hello".getBytes(),-1).getOperation();
+
+ assert userAddress.equals(eventContext.getLedger().getUser(hashDigest,userAddress).getAddress()) :
+ "getUser(hash,address), expect!=acutal";
+
+ assert contractAddress.equals(eventContext.getLedger().getContract(hashDigest,contractAddress).getAddress()) :
+ "getContract(hash,address), expect!=acutal";
+
+ PubKey userPubKey = new PubKey(CryptoAlgorithm.ED25519, userPubKeyVal.getBytes());
+ BlockchainIdentity userBlockId = new BlockchainIdentityData(userPubKey);
+ contractEventContext.getLedger().users().register(userBlockId);
+
+// txRootHash
+// eventContext.getLedger().getTransactions(hashDigest,ledgerBlock1.getHash(),0,10);
+
+ HashDigest txHashDigest = new HashDigest(Base58Utils.decode(txHash));
+ LedgerTransaction ledgerTransactions = eventContext.getLedger().getTransactionByContentHash(hashDigest,txHashDigest);
+ assert ledgerTransactions != null : "getTransactionByContentHash(hashDigest,txHashDigest),expect!=acutal";
+
+ System.out.println("issue(),over.");
+ }
+
+ @ContractEvent(name = "transfer-asset")
+ public void transfer(ContractEventContext contractEventContext) {
+ byte[] args = contractEventContext.getArgs();
+ String[] argStr = new String(args).split(BaseConstant.DELIMETER_DOUBLE_ALARM);
+ String fromAddress = argStr[0];
+ String toAddress = argStr[1];
+ long amount = Long.parseLong(argStr[2]);
+
+ if (amount < 0) {
+ throw new ContractException("The amount is negative!");
+ }
+ if (amount == 0) {
+ return;
+ }
+ checkSignerPermission(fromAddress);
+ System.out.println("transfer(),over.");
+ }
+
+ private void checkAllOwnersAgreementPermission() {
+ Set owners = eventContext.getContracOwners();
+ Set requestors = eventContext.getTxSigners();
+ if (requestors.size() != owners.size()) {
+ throw new ContractException("Permission Error! -- The requestors is not exactlly being owners!");
+ }
+
+ Map ownerMap = new HashMap<>();
+ for (BlockchainIdentity o : owners) {
+ ownerMap.put(o.getAddress().toBase58(), o);
+ }
+ for (BlockchainIdentity r : requestors) {
+ System.out.println("checkAllOwnersAgreementPermission(),r.getAddress:"+r.getAddress());
+ if (!ownerMap.containsKey(r.getAddress())) {
+ throw new ContractException("Permission Error! -- No agreement of all owners!");
+ }
+ }
+ }
+
+ private void checkSignerPermission(String address) {
+ Set requestors = eventContext.getTxSigners();
+ for (BlockchainIdentity r : requestors) {
+ if (r.getAddress().equals(address)) {
+ return;
+ }
+ }
+// throw new ContractError("Permission Error! -- No signature !");
+ }
+
+}
diff --git a/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract2.java b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract2.java
new file mode 100644
index 00000000..247e36e8
--- /dev/null
+++ b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract2.java
@@ -0,0 +1,62 @@
+package com.jd.blockchain.contract;
+
+import com.jd.blockchain.contract.model.*;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.KVDataEntry;
+import com.jd.blockchain.utils.BaseConstant;
+
+/**
+ * mock the smart contract;
+ */
+@Contract
+public class AssetContract2 implements EventProcessingAwire {
+ // private static final Logger LOGGER = LoggerFactory.getLogger(AssetContract.class);
+ private static final String KEY_TOTAL = "TOTAL";
+ private static final String LEDGER_HASH = "ledgerHash";
+
+ @ContractEvent(name = "issue-asset")
+ public void test1(ContractEventContext eventContext) throws Exception{
+ byte [] args_ = eventContext.getArgs();
+ if(args_ == null){
+ return;
+ }
+ String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM);
+
+ long amount = Long.parseLong(args[0]);
+ long amount1 = Long.parseLong(args[1]);
+ String contractDataAddress = args[2];
+ System.out.println("in contract2,invoke test1(),amountAdd:"+(amount+amount1)+",contractDataAddress="+contractDataAddress);
+
+ //test invoke;
+ HashDigest hashDigest = eventContext.getCurrentLedgerHash();
+ KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(hashDigest, contractDataAddress,
+ KEY_TOTAL,LEDGER_HASH); //,"latestBlockHash"
+
+ //
+// assert ByteArray.toHex("total value,dataAccount".getBytes()).equals(kvEntries[0].getValue())
+// && ByteArray.toHex("abc value,dataAccount".getBytes()).equals(kvEntries[1].getValue()) :
+// "getDataEntries() test,expect=actual;";
+ System.out.println("in dataSet,KEY_TOTAL="+new String(kvEntries[0].getValue().toString()));
+ System.out.println("in dataSet,LEDGER_HASH="+new String(kvEntries[1].getValue().toString()));
+ }
+
+ @Override
+ public void beforeEvent(ContractEventContext contractEventContext) {
+
+ }
+
+ @Override
+ public void postEvent() {
+
+ }
+
+ @Override
+ public void postEvent(ContractEventContext contractEventContext, ContractException contractError) {
+
+ }
+
+ @Override
+ public void postEvent(ContractException contractError) {
+
+ }
+}
diff --git a/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract3.java b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract3.java
new file mode 100644
index 00000000..acae4716
--- /dev/null
+++ b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract3.java
@@ -0,0 +1,65 @@
+package com.jd.blockchain.contract;
+
+import com.jd.blockchain.contract.model.*;
+import com.jd.blockchain.utils.BaseConstant;
+
+/**
+ * mock the smart contract;
+ * Do only the simplest addition operation.
+ */
+@Contract
+public class AssetContract3 implements EventProcessingAwire {
+
+ @ContractEvent(name = "issue-asset")
+ public void test1(ContractEventContext eventContext){
+ byte [] args_ = eventContext.getArgs();
+ if(args_ == null){
+ return;
+ }
+
+// KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(),
+// "", "");
+ String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM);
+
+ long amount = Long.parseLong(args[0]);
+ long amount1 = Long.parseLong(args[1]);
+ String contractDataAddress = args[2];
+ System.out.println("###@@@,in contract3,invoke test1(),amountAdd:"+(amount+amount1)+
+ ",contractDataAddress= "+contractDataAddress);
+ }
+
+ @ContractEvent(name = "multi")
+ public void test2(ContractEventContext eventContext) throws Exception{
+ byte [] args_ = eventContext.getArgs();
+ if(args_ == null){
+ return;
+ }
+ String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM);
+
+ long amount = Long.parseLong(args[0]);
+ long amount1 = Long.parseLong(args[1]);
+ String contractDataAddress = args[2];
+ System.out.println("###test,in contract3,invoke test2(),amount Multi:"+(amount*amount1)+
+ ",contractDataAddress= "+contractDataAddress);
+ }
+
+ @Override
+ public void beforeEvent(ContractEventContext contractEventContext) {
+
+ }
+
+ @Override
+ public void postEvent() {
+
+ }
+
+ @Override
+ public void postEvent(ContractEventContext contractEventContext, ContractException contractError) {
+
+ }
+
+ @Override
+ public void postEvent(ContractException contractError) {
+
+ }
+}
diff --git a/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract4.java b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract4.java
new file mode 100644
index 00000000..0b2766b0
--- /dev/null
+++ b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract4.java
@@ -0,0 +1,83 @@
+package com.jd.blockchain.contract;
+
+import com.jd.blockchain.contract.model.*;
+import com.jd.blockchain.ledger.BlockchainKeyGenerator;
+import com.jd.blockchain.ledger.BlockchainKeyPair;
+import com.jd.blockchain.ledger.KVDataEntry;
+import com.jd.blockchain.utils.BaseConstant;
+import com.jd.blockchain.utils.io.ByteArray;
+
+/**
+ * mock the smart contract;
+ * The test takes data from the chain and compares it with the expected value;
+ * the value of param1Val should be consistent with that of Integration Test;
+ */
+@Contract
+public class AssetContract4 implements EventProcessingAwire {
+ String param1 = "param1";
+ String param1Val = "param1Val";
+
+ @ContractEvent(name = "issue-asset")
+ public void test1(ContractEventContext eventContext) throws Exception{
+ byte [] args_ = eventContext.getArgs();
+ if(args_ == null){
+ return;
+ }
+ String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM);
+
+ long amount = Long.parseLong(args[0]);
+ long amount1 = Long.parseLong(args[1]);
+ String contractDataAddress = args[2];
+ System.out.println("###@@@,in contract4,invoke test1(),amountAdd:"+(amount+amount1)+
+ ",contractDataAddress= "+contractDataAddress);
+
+ BlockchainKeyPair dataAccount = BlockchainKeyGenerator.getInstance().generate();
+// contractEventContext.getLedger().dataAccounts().register(dataAccount.getIdentity());
+// contractEventContext.getLedger().dataAccount(dataAccount.getAddress()).
+// set(param1,param1Val.getBytes(),-1).getOperation();
+ System.out.println("data address="+dataAccount.getAddress());
+ }
+
+ @ContractEvent(name = "event2")
+ public void test2(ContractEventContext eventContext) throws Exception{
+ byte [] args_ = eventContext.getArgs();
+ if(args_ == null){
+ return;
+ }
+ String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM);
+
+ long amount = Long.parseLong(args[0]);
+ long amount1 = Long.parseLong(args[1]);
+ String contractDataAddress = args[2];
+ System.out.println("###!!!!!!!!,in contract3,invoke test2(),amount Multi:"+(amount*amount1)+
+ ",contractDataAddress= "+contractDataAddress);
+
+ KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(),
+ contractDataAddress, param1);
+ if (ByteArray.toHex(param1Val.getBytes()).equals(kvEntries[0].getValue())){
+ System.out.println("getDataEntries() test,expect==actual;");
+ } else {
+ System.out.println("getDataEntries() test,expect==actual;");
+ }
+ }
+
+ @Override
+ public void beforeEvent(ContractEventContext contractEventContext) {
+
+ }
+
+ @Override
+ public void postEvent() {
+
+ }
+
+ @Override
+ public void postEvent(ContractEventContext contractEventContext, ContractException contractError) {
+
+ }
+
+ @Override
+ public void postEvent(ContractException contractError) {
+
+ }
+}
diff --git a/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract5.java b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract5.java
new file mode 100644
index 00000000..bf3d8def
--- /dev/null
+++ b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract5.java
@@ -0,0 +1,62 @@
+package com.jd.blockchain.contract;
+
+import com.jd.blockchain.contract.model.*;
+import com.jd.blockchain.utils.BaseConstant;
+
+/**
+ * mock the smart contract;
+ * Do only the simplest addition operation.
+ */
+@Contract
+public class AssetContract5 implements EventProcessingAwire {
+
+ @ContractEvent(name = "issue-asset")
+ public void test1(ContractEventContext eventContext) throws Exception{
+ byte [] args_ = eventContext.getArgs();
+ if(args_ == null){
+ return;
+ }
+ String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM);
+
+ long amount = Long.parseLong(args[0]);
+ long amount1 = Long.parseLong(args[1]);
+ String contractDataAddress = args[2];
+ System.out.println("###@@@,in contract5,invoke test1(),amountAdd:"+(amount+amount1)+
+ ",contractDataAddress= "+contractDataAddress);
+ }
+
+ @ContractEvent(name = "multi")
+ public void test2(ContractEventContext eventContext) throws Exception{
+ byte [] args_ = eventContext.getArgs();
+ if(args_ == null){
+ return;
+ }
+ String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM);
+
+ long amount = Long.parseLong(args[0]);
+ long amount1 = Long.parseLong(args[1]);
+ String contractDataAddress = args[2];
+ System.out.println("###test,in contract5,invoke test2(),amount Multi:"+(amount*amount1)+
+ ",contractDataAddress= "+contractDataAddress);
+ }
+
+ @Override
+ public void beforeEvent(ContractEventContext contractEventContext) {
+
+ }
+
+ @Override
+ public void postEvent() {
+
+ }
+
+ @Override
+ public void postEvent(ContractEventContext contractEventContext, ContractException contractError) {
+
+ }
+
+ @Override
+ public void postEvent(ContractException contractError) {
+
+ }
+}
diff --git a/source/contract/contract-framework/pom.xml b/source/contract/contract-framework/pom.xml
new file mode 100644
index 00000000..be2b88ab
--- /dev/null
+++ b/source/contract/contract-framework/pom.xml
@@ -0,0 +1,37 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ contract
+ 0.8.2.RELEASE
+
+ contract-framework
+
+
+
+ com.jd.blockchain
+ contract-model
+ ${project.version}
+
+
+ com.jd.blockchain
+ utils-common
+ ${project.version}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractCode.java b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractCode.java
new file mode 100644
index 00000000..5590cabb
--- /dev/null
+++ b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractCode.java
@@ -0,0 +1,13 @@
+package com.jd.blockchain.contract;
+
+import com.jd.blockchain.contract.model.ContractEventContext;
+
+public interface ContractCode {
+
+ String getAddress();
+
+ long getVersion();
+
+ void processEvent(ContractEventContext eventContext);
+
+}
diff --git a/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractEngine.java b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractEngine.java
new file mode 100644
index 00000000..fe6f6a7a
--- /dev/null
+++ b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractEngine.java
@@ -0,0 +1,32 @@
+package com.jd.blockchain.contract;
+
+/**
+ * contract engine.
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface ContractEngine {
+
+ /**
+ * Returns the contract code for the specified address;
+ *
+ * If not, return null;
+ *
+ * @param address
+ * @return
+ */
+ ContractCode getContract(String address, long version);
+
+ /**
+ * Load contract code;
+ *
+ * If it already exists, it returns the existing instance directly.
+ *
+ * @param address
+ * @param code
+ * @return
+ */
+ ContractCode setupContract(String address, long version, byte[] code);
+
+}
diff --git a/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractServiceProvider.java b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractServiceProvider.java
new file mode 100644
index 00000000..1cc94b5e
--- /dev/null
+++ b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractServiceProvider.java
@@ -0,0 +1,14 @@
+package com.jd.blockchain.contract;
+
+public interface ContractServiceProvider {
+
+ String getName();
+
+ /**
+ * Return the contract code execution engine instance;
+ *
+ * @return
+ */
+ ContractEngine getEngine();
+
+}
diff --git a/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractServiceProviders.java b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractServiceProviders.java
new file mode 100644
index 00000000..f0a6ba37
--- /dev/null
+++ b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractServiceProviders.java
@@ -0,0 +1,41 @@
+package com.jd.blockchain.contract;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.util.ClassUtils;
+
+public class ContractServiceProviders {
+
+ private static final Object mutex = new Object();
+ private static Map providers = new ConcurrentHashMap<>();
+
+ public static ContractServiceProvider getProvider(String className) {
+ ContractServiceProvider provider = providers.get(className);
+ if (provider == null) {
+ synchronized (mutex) {
+ provider = providers.get(className);
+ if (provider == null) {
+ provider = loadProvider(ContractServiceProvider.class, className);
+ providers.put(className, provider);
+ }
+ }
+ }
+ return provider;
+ }
+
+ private static T loadProvider(Class assignableTo, String implementClassName) {
+ Class> providerClass = ClassUtils.resolveClassName(implementClassName,
+ ContractServiceProvider.class.getClassLoader());
+ if (!assignableTo.isAssignableFrom(providerClass)) {
+ throw new IllegalArgumentException(
+ String.format("%s is not implement %s!", implementClassName, assignableTo.getName()));
+ }
+ return BeanUtils.instantiateClass(providerClass, assignableTo);
+ }
+
+ public static void registerProvider(ContractServiceProvider provider) {
+ providers.put(provider.getName(), provider);
+ }
+}
diff --git a/source/contract/contract-jvm/pom.xml b/source/contract/contract-jvm/pom.xml
new file mode 100644
index 00000000..7c36748a
--- /dev/null
+++ b/source/contract/contract-jvm/pom.xml
@@ -0,0 +1,43 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ contract
+ 0.8.2.RELEASE
+
+ contract-jvm
+
+
+
+ com.jd.blockchain
+ contract-framework
+ ${project.version}
+
+
+ com.jd.blockchain
+ runtime-context
+ ${project.version}
+
+
+ com.jd.blockchain
+ runtime-modular
+ ${project.version}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JVMContractEngine.java b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JVMContractEngine.java
new file mode 100644
index 00000000..6769b618
--- /dev/null
+++ b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JVMContractEngine.java
@@ -0,0 +1,41 @@
+package com.jd.blockchain.contract.jvm;
+
+import com.jd.blockchain.contract.ContractCode;
+import com.jd.blockchain.contract.ContractEngine;
+import com.jd.blockchain.runtime.Module;
+import com.jd.blockchain.runtime.RuntimeContext;
+
+public class JVMContractEngine implements ContractEngine {
+
+ private RuntimeContext runtimeContext = RuntimeContext.get();
+// private RuntimeContext runtimeContext = ModularRuntimeContext.setup(System.getProperty("user.dir"));
+
+ private String getCodeName(String address, long version) {
+ return address + "_" + version;
+ }
+
+ @Override
+ public ContractCode getContract(String address, long version) {
+ String codeName = getCodeName(address, version);
+ Module module = runtimeContext.getDynamicModule(codeName);
+ if (module == null) {
+ return null;
+ }
+ return new JavaContractCode(address, version, module);
+ }
+
+ @Override
+ public ContractCode setupContract(String address, long version, byte[] code) {
+ //is there the contractCode before setup? if yes ,then return;
+ ContractCode contractCode = getContract(address,version);
+ if(contractCode != null){
+ return contractCode;
+ }
+ String codeName = getCodeName(address, version);
+ Module module = runtimeContext.createDynamicModule(codeName,code);
+ if (module == null) {
+ return null;
+ }
+ return new JavaContractCode(address, version, module);
+ }
+}
diff --git a/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JVMContractServiceProvider.java b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JVMContractServiceProvider.java
new file mode 100644
index 00000000..859c25c7
--- /dev/null
+++ b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JVMContractServiceProvider.java
@@ -0,0 +1,20 @@
+package com.jd.blockchain.contract.jvm;
+
+import com.jd.blockchain.contract.ContractEngine;
+import com.jd.blockchain.contract.ContractServiceProvider;
+
+public class JVMContractServiceProvider implements ContractServiceProvider {
+ @Override
+ public String getName() {
+ return JVMContractServiceProvider.class.getName();
+ }
+
+ @Override
+ public ContractEngine getEngine() {
+ return InnerEngine.INSTANCE;
+ }
+
+ private static class InnerEngine {
+ private static final ContractEngine INSTANCE = new JVMContractEngine();
+ }
+}
diff --git a/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java
new file mode 100644
index 00000000..282f06c6
--- /dev/null
+++ b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java
@@ -0,0 +1,123 @@
+package com.jd.blockchain.contract.jvm;
+
+import com.jd.blockchain.contract.ContractCode;
+import com.jd.blockchain.contract.model.ContractEvent;
+import com.jd.blockchain.contract.model.ContractEventContext;
+import com.jd.blockchain.runtime.Module;
+import com.jd.blockchain.utils.BaseConstant;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.ReflectionUtils;
+
+import static com.jd.blockchain.utils.BaseConstant.CONTRACT_MAIN_CLASS_KEY;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * contract code based jvm
+ * @author zhaogw
+ */
+public class JavaContractCode implements ContractCode {
+ private static final Logger LOGGER = LoggerFactory.getLogger(JavaContractCode.class);
+ private Module codeModule;
+ private String address;
+ private long version;
+ private ContractEventContext contractEventContext;
+
+ public JavaContractCode(String address, long version, Module codeModule) {
+ this.address = address;
+ this.version = version;
+ this.codeModule = codeModule;
+ }
+
+ @Override
+ public String getAddress() {
+ return address;
+ }
+
+ @Override
+ public long getVersion() {
+ return version;
+ }
+
+ @Override
+ public void processEvent(ContractEventContext eventContext) {
+ this.contractEventContext = eventContext;
+ codeModule.execute(new ContractThread());
+ }
+
+ class ContractThread implements Runnable{
+ @Override
+ public void run(){
+ LOGGER.info("ContractThread execute().");
+ try {
+ //Perform pretreatment;
+ long startTime = System.currentTimeMillis();
+
+ String contractClassName = codeModule.getMainClass();
+ Class myClass = codeModule.loadClass(contractClassName);
+ Object contractMainClassObj = myClass.newInstance();
+
+ Method beforeMth_ = myClass.getMethod("beforeEvent",codeModule.loadClass(ContractEventContext.class.getName()));
+ ReflectionUtils.invokeMethod(beforeMth_,contractMainClassObj,contractEventContext);
+ LOGGER.info("beforeEvent,spend time:"+(System.currentTimeMillis()-startTime));
+
+ Method eventMethod = this.getMethodByAnno(contractMainClassObj,contractEventContext.getEvent());
+ startTime = System.currentTimeMillis();
+
+ ReflectionUtils.invokeMethod(eventMethod,contractMainClassObj,contractEventContext);
+
+ LOGGER.info("execute contract,spend time:"+(System.currentTimeMillis()-startTime));
+
+ Method mth2 = myClass.getMethod("postEvent");
+ startTime = System.currentTimeMillis();
+ ReflectionUtils.invokeMethod(mth2,contractMainClassObj);
+ LOGGER.info("postEvent,spend time:"+(System.currentTimeMillis()-startTime));
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ //get the relation between the methods and annotations
+ Method getMethodByAnno(Object classObj, String eventName){
+ Class> c = classObj.getClass();
+ Class contractEventClass = null;
+ try {
+ contractEventClass = (Class )c.getClassLoader().loadClass(ContractEvent.class.getName());
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ Method[] classMethods = c.getMethods();
+ Map methodAnnoMap = new HashMap();
+ Map annoMethodMap = new HashMap();
+ for(int i = 0;i
+ 4.0.0
+
+ com.jd.blockchain
+ contract
+ 0.8.2.RELEASE
+
+ contract-maven-plugin
+ maven-plugin
+
+ ${parent.version}
+ ${parent.version}
+ 4.12
+
+
+ 0.8.2.RELEASE
+
+
+ junit
+ junit
+ test
+
+
+
+ com.jd.blockchain
+ contract-model
+ ${contract.version}
+
+
+
+ com.jd.blockchain
+ utils-common
+ ${utils.version}
+
+
+
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ 3.6.0
+ provided
+
+
+
+ org.apache.maven
+ maven-plugin-api
+ 2.0
+
+
+ org.apache.maven
+ maven-project
+ 2.0.6
+
+
+
+ com.github.javaparser
+ javaparser-core
+ ${javaparser.version}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-plugin-plugin
+ 3.5
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.8
+ 1.8
+ UTF-8
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/contract/contract-maven-plugin/readme.txt b/source/contract/contract-maven-plugin/readme.txt
new file mode 100644
index 00000000..46697cce
--- /dev/null
+++ b/source/contract/contract-maven-plugin/readme.txt
@@ -0,0 +1,19 @@
+说明
+1.编译:mvn clean install
+
+快速自测:
+1.ContractRemoteAutoMojoTest类用于快速自测发布和执行,快速自测是在测试链的环境中发布和执行合约;
+2.修改sys-contract.properties文件中的相关信息;
+3.合约发布之后,会在控制台生成合约地址,待5秒钟之后,会执行此合约。sys-contract.properties的contractArgs参数可修改,查看其不同效果;
+###
+contract's address=5SmEqUsnLY4APVfS32xYDpRPuz55Rsuupdt1
+execute the contract,result=true
+exeContract(),SUCCESS
+###
+4.在peer节点的控制台可以看到输出的结果信息。
+
+通过maven插件中通过ContractAllAutoMojo做简单的编译、发布和执行测试,对应单元测试类ContractAllAutoMojoTest;
+
+
+
+
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/CheckImportsMojo.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/CheckImportsMojo.java
new file mode 100644
index 00000000..86747f9d
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/CheckImportsMojo.java
@@ -0,0 +1,60 @@
+package com.jd.blockchain;
+
+import com.github.javaparser.JavaParser;
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.ImportDeclaration;
+import com.github.javaparser.ast.NodeList;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
+
+@Mojo(name = "checkImports")
+public class CheckImportsMojo extends AbstractMojo {
+ Logger logger = LoggerFactory.getLogger(CheckImportsMojo.class);
+
+ @Parameter(defaultValue = "${project}", required = true, readonly = true)
+ private MavenProject project;
+
+ @Override
+ public void execute() throws MojoFailureException {
+ List sources;
+ try {
+ InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("config.properties");
+ Properties properties = new Properties();
+ properties.load(inputStream);
+ String[] packageBlackList = properties.getProperty("blacklist").split(",");
+ Path baseDirPath = project.getBasedir().toPath();
+ sources = Files.find(baseDirPath, Integer.MAX_VALUE, (file, attrs) -> (file.toString().endsWith(".java"))).collect(Collectors.toList());
+ for (Path path : sources) {
+ CompilationUnit compilationUnit = JavaParser.parse(path);
+ NodeList imports = compilationUnit.getImports();
+ for (ImportDeclaration imp : imports) {
+ String importName = imp.getName().asString();
+ for (String item : packageBlackList) {
+ if (importName.startsWith(item)) {
+ throw new MojoFailureException("Inclusion of this import package is not allowed in source code:" + importName);
+ }
+ }
+ }
+ }
+ } catch (IOException exception) {
+ logger.error(exception.getMessage());
+ throw new MojoFailureException("IO ERROR");
+ } catch (NullPointerException e) {
+ logger.error(e.getMessage());
+ }
+ }
+}
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractDeployMojo.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractDeployMojo.java
new file mode 100644
index 00000000..fa51d3ef
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractDeployMojo.java
@@ -0,0 +1,119 @@
+package com.jd.blockchain;
+
+import com.jd.blockchain.contract.model.ContractDeployExeUtil;
+import com.jd.blockchain.crypto.asymmetric.PrivKey;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.BlockchainKeyPair;
+import com.jd.blockchain.tools.keygen.KeyGenCommand;
+import com.jd.blockchain.utils.codec.Base58Utils;
+import com.jd.blockchain.utils.io.FileUtils;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+/**
+ * for contract remote deploy;
+ * @phase compile
+ * @author zhaogw
+ * date 2018/10/18 10:12
+ */
+
+@Mojo(name = "contractDeploy")
+public class ContractDeployMojo extends AbstractMojo {
+ Logger logger = LoggerFactory.getLogger(ContractDeployMojo.class);
+
+ @Parameter
+ private File config;
+
+ @Override
+ public void execute()throws MojoFailureException {
+ Properties prop = new Properties();
+ InputStream input = null;
+
+ try {
+ input = new FileInputStream(config);
+ prop.load(input);
+
+ } catch (IOException ex) {
+ logger.error(ex.getMessage());
+ throw new MojoFailureException("io error");
+ } finally {
+ if (input != null) {
+ try {
+ input.close();
+ } catch (IOException e) {
+ logger.error(e.getMessage());
+ }
+ }
+ }
+ int port;
+ try {
+ port = Integer.parseInt(prop.getProperty("port"));
+ }catch (NumberFormatException e){
+ logger.error(e.getMessage());
+ throw new MojoFailureException("invalid port");
+ }
+ String host = prop.getProperty("host");
+ String ledger = prop.getProperty("ledgerHash");
+ String ownerPubPath = prop.getProperty("ownerPubPath");
+ String ownerPrvPath = prop.getProperty("ownerPrvPath");
+ String ownerPassword = FileUtils.readText(prop.getProperty("ownerPassword"));
+ String chainCodePath = prop.getProperty("chainCodePath");
+
+ if(StringUtils.isEmpty(host)){
+ logger.info("host can not be empty");
+ return;
+ }
+
+ if(StringUtils.isEmpty(ledger)){
+ logger.info("ledger can not be empty.");
+ return;
+ }
+ if(StringUtils.isEmpty(ownerPubPath)){
+ logger.info("pubKey can not be empty.");
+ return;
+ }
+ if(StringUtils.isEmpty(ownerPrvPath)){
+ logger.info("prvKey can not be empty.");
+ return;
+ }
+ if(StringUtils.isEmpty(chainCodePath)){
+ logger.info("contractPath can not be empty.");
+ return;
+ }
+
+ File contract = new File(chainCodePath);
+ if (!contract.isFile()){
+ logger.info("file:"+chainCodePath+" is not exist");
+ return;
+ }
+ byte[] contractBytes = FileUtils.readBytes(chainCodePath);
+
+
+// PrivKey prv = KeyGenCommand.decodePrivKeyWithRawPassword(prvKey, password);
+// PubKey pub = KeyGenCommand.decodePubKey(pubKey);
+// BlockchainKeyPair blockchainKeyPair = new BlockchainKeyPair(pub, prv);
+ BlockchainKeyPair ownerKey = ContractDeployExeUtil.instance.getKeyPair(ownerPubPath, ownerPrvPath, ownerPassword);
+ HashDigest ledgerHash = new HashDigest(Base58Utils.decode(ledger));
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("host:"+ host).append(",port:"+port).append(",ledgerHash:"+ledgerHash.toBase58()).
+ append(",pubKey:"+ownerKey.getPubKey()).append(",prvKey:"+ownerKey.getPrivKey()).append(",contractPath:"+chainCodePath);
+ logger.info(sb.toString());
+ if(ContractDeployExeUtil.instance.deploy(host,port,ledgerHash, ownerKey, contractBytes)){
+ logger.info("deploy is OK.");
+ }
+ }
+}
+
+
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/StringUtils.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/StringUtils.java
new file mode 100644
index 00000000..f8fe1867
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/StringUtils.java
@@ -0,0 +1,10 @@
+package com.jd.blockchain;
+/**
+ * @Author zhaogw
+ * @Date 2018/11/26 20:46
+ */
+public abstract class StringUtils {
+ public static boolean isEmpty(Object str) {
+ return str == null || "".equals(str);
+ }
+}
\ No newline at end of file
diff --git a/source/contract/contract-maven-plugin/src/main/resources/config.properties b/source/contract/contract-maven-plugin/src/main/resources/config.properties
new file mode 100644
index 00000000..fdfca23e
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/resources/config.properties
@@ -0,0 +1 @@
+blacklist=java.io,java.net,java.util.Random
\ No newline at end of file
diff --git a/source/contract/contract-maven-plugin/src/main/resources/sys-contract.properties b/source/contract/contract-maven-plugin/src/main/resources/sys-contract.properties
new file mode 100644
index 00000000..6dd66e5e
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/resources/sys-contract.properties
@@ -0,0 +1,19 @@
+#ĿԴļŵλ;
+PROJECT_BASE_DIR=E:\\gitCode\\block\\prototype\\
+#ͬʹõŵλãܲĿУʲȫµĵַ;
+LEDGER_BASE_CLASS_PATH=E:\\gitCode\\block\\prototype\\libs\\
+
+#Ϊ˲ԣʱӵı;
+cParam=com.jd.blockchain.contract.AssetContract3
+sParam=E:\\gitCode\\block\\prototype\\source\\sdk\\contract-sample\\src\\main\\java\\
+eParam=utf-8
+oParam=E:\\gitCode\\block\\prototype\\source\\contract\\contract-maven-plugin\\src\\test\\resources\\
+host=127.0.0.1
+port=8081
+event = issue-asset
+ownerPassword=E:\\gitCode\\block\\prototype\\source\\contract\\contract-maven-plugin\\conf\\ownerPassword.txt
+ownerPubPath=E:\\gitCode\\block\\prototype\\source\\contract\\contract-maven-plugin\\conf\\jd-com.pub
+ownerPrvPath=E:\\gitCode\\block\\prototype\\source\\contract\\contract-maven-plugin\\conf\\jd-com.priv
+chainCodePath=E:\\gitCode\\block\\prototype\\source\\contract\\contract-maven-plugin\\src\\test\\resources\\AssetContract3.contract
+ledgerHash=6FEQDTQMnBGANpfX4haXuWHKHw5cZ6P1h2ocqwckYBxp4
+contractArgs=101##999##abc
\ No newline at end of file
diff --git a/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/CheckImportsMojoTest.java b/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/CheckImportsMojoTest.java
new file mode 100644
index 00000000..9a91e4f6
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/CheckImportsMojoTest.java
@@ -0,0 +1,32 @@
+package com.jd.blockchain.ledger;
+
+import com.jd.blockchain.CheckImportsMojo;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Properties;
+
+/**
+ * @Author zhaogw
+ * @Date 2019/3/1 21:27
+ */
+public class CheckImportsMojoTest {
+ Logger logger = LoggerFactory.getLogger(CheckImportsMojo.class);
+
+ @Test
+ public void test1() {
+ try {
+ InputStream inputStream = CheckImportsMojo.class.getClassLoader().getResourceAsStream("config.properties");
+ Properties properties = new Properties();
+ properties.load(inputStream);
+ String result[] = properties.getProperty("blacklist").split(",");
+ logger.info(Arrays.toString(result).toString());
+ } catch (IOException e) {
+ logger.error(e.getMessage());
+ }
+ }
+}
diff --git a/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractDeployMojoTest.java b/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractDeployMojoTest.java
new file mode 100644
index 00000000..85c02021
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractDeployMojoTest.java
@@ -0,0 +1,24 @@
+package com.jd.blockchain.ledger;
+
+import com.jd.blockchain.ContractDeployMojo;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * for contract deploy and exe;
+ * @Author zhaogw
+ * @Date 2018/11/02 09:06
+ */
+public class ContractDeployMojoTest {
+ private ContractDeployMojo contractDeployMojo = new ContractDeployMojo();
+
+ private void fieldHandle(String fieldName,Object objValue) throws NoSuchFieldException,
+ IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
+ Field field = contractDeployMojo.getClass().getDeclaredField(fieldName);//name为类Instance中的private属性
+ field.setAccessible(true);//=true,可访问私有变量。
+ Class> typeClass = field.getType();
+ field.set(contractDeployMojo, objValue);
+ }
+}
diff --git a/source/contract/contract-maven-plugin/src/test/resources/AssetContract3.contract b/source/contract/contract-maven-plugin/src/test/resources/AssetContract3.contract
new file mode 100644
index 00000000..0874a68f
Binary files /dev/null and b/source/contract/contract-maven-plugin/src/test/resources/AssetContract3.contract differ
diff --git a/source/contract/contract-maven-plugin/src/test/resources/contract.properties b/source/contract/contract-maven-plugin/src/test/resources/contract.properties
new file mode 100644
index 00000000..b0f816f9
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/test/resources/contract.properties
@@ -0,0 +1,3 @@
+#\u5408\u7EA6\u76F8\u5173\u5C5E\u6027
+#Fri Nov 02 17:13:02 CST 2018
+contract=com.jd.blockchain.contract.AssetContract3
diff --git a/source/contract/contract-model/pom.xml b/source/contract/contract-model/pom.xml
new file mode 100644
index 00000000..880c446e
--- /dev/null
+++ b/source/contract/contract-model/pom.xml
@@ -0,0 +1,37 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ contract
+ 0.8.2.RELEASE
+
+ contract-model
+
+
+
+ com.jd.blockchain
+ sdk-client
+ ${project.version}
+
+
+
+ com.jd.blockchain
+ tools-keygen
+ ${project.version}
+
+
+ crypto-framework
+ com.jd.blockchain
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/Contract.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/Contract.java
new file mode 100644
index 00000000..3e79827c
--- /dev/null
+++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/Contract.java
@@ -0,0 +1,14 @@
+package com.jd.blockchain.contract.model;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Contract {
+
+ String name() default "";
+
+}
diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractAppLifecycleAwire.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractAppLifecycleAwire.java
new file mode 100644
index 00000000..e3fd66ae
--- /dev/null
+++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractAppLifecycleAwire.java
@@ -0,0 +1,16 @@
+package com.jd.blockchain.contract.model;
+
+/**
+ * The contract implements this interface to monitor the life cycle events of the contract application.
+ *
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface ContractAppLifecycleAwire extends ContractRuntimeAwire {
+
+ void postConstruct();
+
+ void beforeDestroy();
+
+}
diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractDeployExeUtil.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractDeployExeUtil.java
new file mode 100644
index 00000000..988703c3
--- /dev/null
+++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractDeployExeUtil.java
@@ -0,0 +1,190 @@
+package com.jd.blockchain.contract.model;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.jd.blockchain.binaryproto.DataContractRegistry;
+import com.jd.blockchain.crypto.asymmetric.PrivKey;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.BlockchainIdentity;
+import com.jd.blockchain.ledger.BlockchainIdentityData;
+import com.jd.blockchain.ledger.BlockchainKeyGenerator;
+import com.jd.blockchain.ledger.BlockchainKeyPair;
+import com.jd.blockchain.ledger.ContractCodeDeployOperation;
+import com.jd.blockchain.ledger.ContractEventSendOperation;
+import com.jd.blockchain.ledger.DataAccountKVSetOperation;
+import com.jd.blockchain.ledger.DataAccountRegisterOperation;
+import com.jd.blockchain.ledger.EndpointRequest;
+import com.jd.blockchain.ledger.NodeRequest;
+import com.jd.blockchain.ledger.Operation;
+import com.jd.blockchain.ledger.PreparedTransaction;
+import com.jd.blockchain.ledger.TransactionContent;
+import com.jd.blockchain.ledger.TransactionContentBody;
+import com.jd.blockchain.ledger.TransactionRequest;
+import com.jd.blockchain.ledger.TransactionResponse;
+import com.jd.blockchain.ledger.TransactionTemplate;
+import com.jd.blockchain.ledger.UserRegisterOperation;
+import com.jd.blockchain.sdk.BlockchainService;
+import com.jd.blockchain.sdk.client.GatewayServiceFactory;
+import com.jd.blockchain.tools.keygen.KeyGenCommand;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.codec.Base58Utils;
+import com.jd.blockchain.utils.net.NetworkAddress;
+
+/**
+ * @Author zhaogw
+ * @Date 2018/11/2 10:18
+ */
+public enum ContractDeployExeUtil {
+ instance;
+ private BlockchainService bcsrv;
+ private Bytes contractAddress;
+
+ public BlockchainKeyPair getKeyPair(String pubPath, String prvPath, String rawPassword){
+ PubKey pub = null;
+ PrivKey prv = null;
+ try {
+ prv = KeyGenCommand.readPrivKey(prvPath, KeyGenCommand.encodePassword(rawPassword));
+ pub = KeyGenCommand.readPubKey(pubPath);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return new BlockchainKeyPair(pub, prv);
+ }
+
+ public PubKey getPubKey(String pubPath){
+ PubKey pub = null;
+ try {
+ if(pubPath == null){
+ BlockchainKeyPair contractKeyPair = BlockchainKeyGenerator.getInstance().generate();
+ pub = contractKeyPair.getPubKey();
+ }else {
+ pub = KeyGenCommand.readPubKey(pubPath);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return pub;
+ }
+ public byte[] getChainCode(String path){
+ byte[] chainCode = null;
+ File file = null;
+ InputStream input = null;
+ try {
+ file = new File(path);
+ input = new FileInputStream(file);
+ chainCode = new byte[input.available()];
+ input.read(chainCode);
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if(input!=null){
+ input.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return chainCode;
+ }
+
+ private void register(){
+ DataContractRegistry.register(TransactionContent.class);
+ DataContractRegistry.register(TransactionContentBody.class);
+ DataContractRegistry.register(TransactionRequest.class);
+ DataContractRegistry.register(NodeRequest.class);
+ DataContractRegistry.register(EndpointRequest.class);
+ DataContractRegistry.register(TransactionResponse.class);
+ DataContractRegistry.register(DataAccountKVSetOperation.class);
+ DataContractRegistry.register(DataAccountKVSetOperation.KVWriteEntry.class);
+ DataContractRegistry.register(Operation.class);
+ DataContractRegistry.register(ContractCodeDeployOperation.class);
+ DataContractRegistry.register(ContractEventSendOperation.class);
+ DataContractRegistry.register(DataAccountRegisterOperation.class);
+ DataContractRegistry.register(UserRegisterOperation.class);
+ }
+
+ public BlockchainService initBcsrv(String host, int port) {
+ if(bcsrv!=null){
+ return bcsrv;
+ }
+ NetworkAddress addr = new NetworkAddress(host, port);
+ GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(addr);
+ bcsrv = gwsrvFact.getBlockchainService();
+ return bcsrv;
+ }
+
+ public boolean deploy(HashDigest ledgerHash, BlockchainIdentity contractIdentity, BlockchainKeyPair ownerKey, byte[] chainCode){
+ register();
+
+ TransactionTemplate txTpl = bcsrv.newTransaction(ledgerHash);
+ txTpl.contracts().deploy(contractIdentity, chainCode);
+ PreparedTransaction ptx = txTpl.prepare();
+ ptx.sign(ownerKey);
+ // Submit and wait for consensus to return;
+ TransactionResponse txResp = ptx.commit();
+
+ // Verification results;
+ contractAddress = contractIdentity.getAddress();
+ this.setContractAddress(contractAddress);
+ System.out.println("contract's address="+contractAddress);
+ return txResp.isSuccess();
+ }
+ public boolean deploy(String host, int port, HashDigest ledgerHash, BlockchainKeyPair ownerKey, byte[] chainCode){
+ register();
+
+ BlockchainIdentity contractIdentity = BlockchainKeyGenerator.getInstance().generate().getIdentity();
+ initBcsrv(host,port);
+ return deploy(ledgerHash, contractIdentity, ownerKey, chainCode);
+ }
+
+ // Generate contract addresses based on user-specified public keys
+ public boolean deploy(String host, int port, String ledger,String ownerPubPath, String ownerPrvPath,
+ String ownerPassword, String chainCodePath,String pubPath){
+ PubKey pubKey = getPubKey(pubPath);
+ BlockchainIdentity contractIdentity = new BlockchainIdentityData(pubKey);
+ byte[] chainCode = getChainCode(chainCodePath);
+
+ BlockchainKeyPair ownerKey = getKeyPair(ownerPubPath, ownerPrvPath, ownerPassword);
+ HashDigest ledgerHash = new HashDigest(Base58Utils.decode(ledger));
+ initBcsrv(host,port);
+ return deploy(ledgerHash, contractIdentity, ownerKey, chainCode);
+ }
+
+ public boolean exeContract(String ledger,String ownerPubPath, String ownerPrvPath,
+ String ownerPassword,String event,String contractArgs){
+ BlockchainKeyPair ownerKey = getKeyPair(ownerPubPath, ownerPrvPath, ownerPassword);
+ HashDigest ledgerHash = new HashDigest(Base58Utils.decode(ledger));
+
+ // Define the transaction, transfer the simplest numbers, strings, extract the address in the contract;
+ TransactionTemplate txTpl = bcsrv.newTransaction(ledgerHash);
+ txTpl.contractEvents().send(getContractAddress(),event,contractArgs.getBytes());
+
+ // sign;
+ PreparedTransaction ptx = txTpl.prepare();
+ ptx.sign(ownerKey);
+
+ // Submit and wait for consensus to return;
+ TransactionResponse txResp = ptx.commit();
+
+ // Verification results;
+ return txResp.isSuccess();
+ }
+
+ public Bytes getContractAddress() {
+ return contractAddress;
+ }
+
+ public void setContractAddress(Bytes contractAddress) {
+ this.contractAddress = contractAddress;
+ }
+}
diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractEvent.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractEvent.java
new file mode 100644
index 00000000..00f24e76
--- /dev/null
+++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractEvent.java
@@ -0,0 +1,14 @@
+package com.jd.blockchain.contract.model;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ContractEvent {
+
+ String name() default "";
+
+}
diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractEventContext.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractEventContext.java
new file mode 100644
index 00000000..6104a1e6
--- /dev/null
+++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractEventContext.java
@@ -0,0 +1,64 @@
+package com.jd.blockchain.contract.model;
+
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.BlockchainIdentity;
+import com.jd.blockchain.ledger.TransactionRequest;
+
+import java.util.Set;
+
+
+public interface ContractEventContext {
+
+ /**
+ * current ledger hash;
+ *
+ * @return
+ */
+ HashDigest getCurrentLedgerHash();
+
+ /**
+ * Transaction requests for execution of contract events;
+ *
+ * @return
+ */
+ TransactionRequest getTransactionRequest();
+
+ /**
+ * Collection of signatories of the transaction;
+ *
+ * @return
+ */
+ Set getTxSigners();
+
+ /**
+ * event name;
+ *
+ * @return
+ */
+ String getEvent();
+
+ /**
+ * param list;
+ *
+ * @return
+ */
+ byte[] getArgs();
+
+ /**
+ * ledger operation context;
+ *
+ * @return
+ */
+ LedgerContext getLedger();
+
+ /**
+ * Collection of Contracts Owners;
+ *
+ *
+ * The owner of the contract is the signer when the contract is deployed.
+ *
+ * @return
+ */
+ Set getContracOwners();
+
+}
diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractException.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractException.java
new file mode 100644
index 00000000..0e13d2c5
--- /dev/null
+++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractException.java
@@ -0,0 +1,16 @@
+package com.jd.blockchain.contract.model;
+
+public class ContractException extends RuntimeException {
+
+ public ContractException(String message) {
+ super(message);
+ }
+
+ public ContractException(String message,ErrorCodeEnum errorCodeEnum) {
+ super(message+","+errorCodeEnum.toString());
+ }
+
+ public ContractException(ErrorCodeEnum errorCodeEnum) {
+ super(errorCodeEnum.toString());
+ }
+}
diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractRuntimeAwire.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractRuntimeAwire.java
new file mode 100644
index 00000000..f60eff3b
--- /dev/null
+++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractRuntimeAwire.java
@@ -0,0 +1,10 @@
+package com.jd.blockchain.contract.model;
+
+/**
+ * The sub-interface of contract implementation {@link ContractRuntimeAwire} can listen for life cycle events at runtime.
+ * @author huanghaiquan
+ *
+ */
+public interface ContractRuntimeAwire {
+
+}
diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ErrorCodeEnum.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ErrorCodeEnum.java
new file mode 100644
index 00000000..c507af4d
--- /dev/null
+++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ErrorCodeEnum.java
@@ -0,0 +1,52 @@
+package com.jd.blockchain.contract.model;
+
+/**
+ * ErrorCodeEnum
+ * @author zhaogw
+ * date 2018/11/8 15:32
+ */
+public enum ErrorCodeEnum {
+ //<100为fatal error;
+ GATEWAY_CONNECT_ERROR(1,ErrorType.ERROR,"GatewayServiceFactory connect error.!"),
+ CONTRACT_CLASSPATH_NOT_SET(2,ErrorType.ERROR,"in private contract classLoader,no jar in the contract folder!"),
+ //Other errors are counted from 101.
+ AMOUNT_NEGATIVE(101,ErrorType.ALARM,"The amount is negative!");
+
+
+ private int code;
+ private int type;
+ private String message;
+
+ ErrorCodeEnum(int code, int type, String message) {
+ this.code = code;
+ this.type = type;
+ this.message = message;
+ }
+
+ public int getCode() {
+ return code;
+ }
+ public int getType() {
+ return type;
+ }
+ public String getMessage() {
+ return message;
+ }
+
+ @Override
+ public String toString(){
+ return "code:"+code+", type:"+type+", message:"+message;
+ }
+}
+
+/**
+ * Classify the errors so that they can be summarized easily.
+ */
+class ErrorType {
+ public static final int ALARM = 0;
+ public static final int ERROR = 1;
+ public static final int CONTRACT_EXE = 2;
+ public static final int CONTRACT_COMPILE = 3;
+}
+
+
diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/EventHandle.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/EventHandle.java
new file mode 100644
index 00000000..3467d031
--- /dev/null
+++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/EventHandle.java
@@ -0,0 +1,20 @@
+package com.jd.blockchain.contract.model;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * HTTP service method;
+ *
+ * @author haiq
+ *
+ */
+@Target({ ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EventHandle {
+
+
+
+}
diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/EventProcessingAwire.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/EventProcessingAwire.java
new file mode 100644
index 00000000..7e96bb96
--- /dev/null
+++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/EventProcessingAwire.java
@@ -0,0 +1,41 @@
+package com.jd.blockchain.contract.model;
+
+/**
+ * @author huanghaiquan
+ *
+ */
+public interface EventProcessingAwire extends ContractRuntimeAwire {
+
+ /**
+ * Called before the event handling method is executed;
+ *
+ * @param eventContext
+ */
+ void beforeEvent(ContractEventContext eventContext);
+
+ /**
+ * Called after the event handling method is successfully executed;
+ *
+ * @param eventContext
+ * evenet;
+ * @param error
+ * Error; if the event processing ends normally, this parameter is null;
+ * if an event processing error occurs, this parameter is not empty;
+ */
+ void postEvent(ContractEventContext eventContext, ContractException error);
+
+
+ /**
+ * Called after the event handling method is successfully executed;
+ *
+ * @param error
+ * Error; if the event processing ends normally, this parameter is null;
+ * if an event processing error occurs, this parameter is not empty;
+ */
+ void postEvent(ContractException error);
+
+ /**
+ * Called after the event handling method is successfully executed;
+ */
+ void postEvent();
+}
diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/LedgerContext.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/LedgerContext.java
new file mode 100644
index 00000000..5f2fbcbc
--- /dev/null
+++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/LedgerContext.java
@@ -0,0 +1,11 @@
+package com.jd.blockchain.contract.model;
+
+import com.jd.blockchain.ledger.data.DataAccountOperator;
+import com.jd.blockchain.ledger.data.UserOperator;
+import com.jd.blockchain.sdk.BlockchainQueryService;
+
+public interface LedgerContext extends BlockchainQueryService, UserOperator, DataAccountOperator{
+
+
+
+}
diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/LocalContractEventContext.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/LocalContractEventContext.java
new file mode 100644
index 00000000..4da536cd
--- /dev/null
+++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/LocalContractEventContext.java
@@ -0,0 +1,108 @@
+package com.jd.blockchain.contract.model;
+
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.BlockchainIdentity;
+import com.jd.blockchain.ledger.TransactionRequest;
+import com.jd.blockchain.utils.io.ByteArray;
+
+import java.util.Set;
+
+/**
+ * @Author zhaogw
+ * @Date 2018/9/5 17:43
+ */
+public class LocalContractEventContext implements ContractEventContext,Cloneable {
+ private HashDigest ledgeHash;
+ private String event;
+ private byte[] chainCode;
+ private byte[] args;
+ private TransactionRequest transactionRequest;
+ private Set txSigners;
+ private Set contractOwners;
+ private LedgerContext ledgerContext;
+
+ public LocalContractEventContext(HashDigest ledgeHash, byte[] chainCode, String event){
+ this.ledgeHash = ledgeHash;
+ this.event = event;
+ this.chainCode = chainCode;
+ }
+
+ @Override
+ public HashDigest getCurrentLedgerHash() {
+ return ledgeHash;
+ }
+
+ @Override
+ public TransactionRequest getTransactionRequest() {
+ return transactionRequest;
+ }
+
+ @Override
+ public Set getTxSigners() {
+ return txSigners;
+ }
+
+ @Override
+ public String getEvent() {
+ return event;
+ }
+
+ @Override
+ public byte[] getArgs() {
+ return args;
+ }
+
+ @Override
+ public LedgerContext getLedger() {
+ return ledgerContext;
+ }
+
+ @Override
+ public Set getContracOwners() {
+ return null;
+ }
+
+ public LocalContractEventContext setLedgeHash(HashDigest ledgeHash) {
+ this.ledgeHash = ledgeHash;
+ return this;
+ }
+
+ public LocalContractEventContext setEvent(String event) {
+ this.event = event;
+ return this;
+ }
+
+ public LocalContractEventContext setTransactionRequest(TransactionRequest transactionRequest) {
+ this.transactionRequest = transactionRequest;
+ return this;
+ }
+
+ public LocalContractEventContext setTxSigners(Set txSigners) {
+ this.txSigners = txSigners;
+ return this;
+ }
+
+ public LocalContractEventContext setContractOwners(Set contractOwners) {
+ this.contractOwners = contractOwners;
+ return this;
+ }
+
+ public LocalContractEventContext setLedgerContext(LedgerContext ledgerContext) {
+ this.ledgerContext = ledgerContext;
+ return this;
+ }
+
+ public byte[] getChainCode() {
+ return chainCode;
+ }
+
+ public LocalContractEventContext setChainCode(byte[] chainCode) {
+ this.chainCode = chainCode;
+ return this;
+ }
+
+ public LocalContractEventContext setArgs(byte[] args) {
+ this.args = args;
+ return this;
+ }
+}
diff --git a/source/contract/contract-model/src/main/resources/sys-contract.properties b/source/contract/contract-model/src/main/resources/sys-contract.properties
new file mode 100644
index 00000000..e8aee795
--- /dev/null
+++ b/source/contract/contract-model/src/main/resources/sys-contract.properties
@@ -0,0 +1,7 @@
+#项目源文件存放的位置;
+PROJECT_BASE_DIR=e:\\gitCode\\block\\prototype\\
+#合同使用的类库存放的位置,可能不在项目中,故采用全新的地址;
+LEDGER_BASE_CLASS_PATH=e:\\gitCode\\block\\prototype\\libs\\
+BLACKLIST=java.io;java.net;java.util.Random
+#type:public,private
+CONTRACT_CLASSLOADER_TYPE=private
\ No newline at end of file
diff --git a/source/contract/pom.xml b/source/contract/pom.xml
new file mode 100644
index 00000000..ba9ac007
--- /dev/null
+++ b/source/contract/pom.xml
@@ -0,0 +1,22 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ jdchain-root
+ 0.8.2.RELEASE
+
+ contract
+ pom
+
+
+ contract-model
+ contract-framework
+ contract-jvm
+ contract-maven-plugin
+ contract-compile
+
+
+
+
+
\ No newline at end of file
diff --git a/source/crypto/crypto-adv/pom.xml b/source/crypto/crypto-adv/pom.xml
new file mode 100644
index 00000000..ac213acd
--- /dev/null
+++ b/source/crypto/crypto-adv/pom.xml
@@ -0,0 +1,75 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ crypto
+ 0.8.2.RELEASE
+
+ crypto-adv
+
+
+
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+
+
+ com.jd.blockchain
+ binary-proto
+ ${project.version}
+
+
+
+ org.powermock
+ powermock-module-junit4
+ 1.6.2
+ test
+
+
+
+ org.powermock
+ powermock-api-mockito
+ 1.6.2
+ test
+
+
+
+ net.i2p.crypto
+ eddsa
+ 0.1.0
+
+
+ net.java.dev.jna
+ jna
+ 5.1.0
+
+
+
+ org.mockito
+ mockito-core
+ 1.10.19
+ test
+
+
+ com.jd.blockchain
+ crypto-framework
+ ${project.version}
+ compile
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/ecvrf/VRF.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/ecvrf/VRF.java
new file mode 100644
index 00000000..0fd713ef
--- /dev/null
+++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/ecvrf/VRF.java
@@ -0,0 +1,102 @@
+package com.jd.blockchain.crypto.ecvrf;
+
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+
+import java.security.SecureRandom;
+import java.util.Objects;
+
+
+public class VRF {
+
+ public static String getLib() throws IllegalArgumentException{
+
+ String lib;
+ String path;
+ String osName = System.getProperty("os.name").toLowerCase();
+
+ // Mac OS
+ if (osName.startsWith("mac")){
+ lib = "libsodium.23.dylib";
+ }
+
+ // Linux OS
+ else if (osName.contains("linux")){
+ lib = "libsodium.so.23.1.0";
+ }
+
+ // unsupported OS
+ else throw new IllegalArgumentException("The VRF implementation is not supported in this Operation System!");
+
+ path = Objects.requireNonNull(VRF.class.getClassLoader().getResource(lib)).getPath();
+ return path;
+ }
+
+ public interface CLibrary extends Library {
+
+ CLibrary INSTANCE = (CLibrary)
+ Native.load((getLib()), CLibrary.class);
+
+ int crypto_vrf_is_valid_key(byte[] pk);
+ int crypto_vrf_keypair_from_seed(byte[] pk, byte[] sk, byte[] seed);
+ int crypto_vrf_prove(byte[] proof, byte[] sk, byte[] m, long mlen);
+ int crypto_vrf_verify(byte[] output, byte[] pk, byte[] proof, byte[] m, long mlen);
+ int crypto_vrf_proof_to_hash(byte[] hash, byte[] proof);
+ void crypto_vrf_ietfdraft03_sk_to_pk(byte[] pk, byte[] sk);
+ }
+
+ public static byte[] genSecretKey(){
+ byte [] seed = new byte[32];
+ byte [] sk = new byte[64];
+ byte [] pk = new byte[32];
+ try{
+ SecureRandom.getInstanceStrong().nextBytes(seed);
+ CLibrary.INSTANCE.crypto_vrf_keypair_from_seed(pk, sk, seed);
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ return sk;
+ }
+
+ public static byte[] sk2pk(byte[] sk){
+ if (sk.length != 64){
+ return null;
+ }
+ byte [] pk = new byte[32];
+ CLibrary.INSTANCE.crypto_vrf_ietfdraft03_sk_to_pk(pk, sk);
+ return pk;
+ }
+
+ public static boolean IsValidPk(byte[] pk){
+ if (pk.length != 32){
+ return false;
+ }
+ return CLibrary.INSTANCE.crypto_vrf_is_valid_key(pk) == 1;
+ }
+
+ public static byte[] prove(byte[] sk, byte[] msg){
+ byte[] proof = new byte[80];
+ if (CLibrary.INSTANCE.crypto_vrf_prove(proof, sk, msg, msg.length)==0){
+ return proof;
+ }
+ return null;
+ }
+
+ public static byte[] proof2hash(byte[] proof){
+ byte[] hash = new byte[64];
+ if (proof.length != 80){
+ return null;
+ }
+ CLibrary.INSTANCE.crypto_vrf_proof_to_hash(hash, proof);
+ return hash;
+ }
+
+ public static boolean verify(byte[] pk, byte[] proof, byte[]msg){
+ byte[] output = new byte[64];
+ if (proof.length != 80 || pk.length != 32){
+ return false;
+ }
+ return CLibrary.INSTANCE.crypto_vrf_verify(output, pk, proof, msg, msg.length) == 0;
+ }
+}
diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/mpc/MultiSum.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/mpc/MultiSum.java
new file mode 100644
index 00000000..4a5dcc9c
--- /dev/null
+++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/mpc/MultiSum.java
@@ -0,0 +1,98 @@
+package com.jd.blockchain.crypto.mpc;
+
+import com.jd.blockchain.crypto.paillier.KeyPair;
+import com.jd.blockchain.crypto.paillier.PaillierUtils;
+import com.jd.blockchain.crypto.paillier.PublicKey;
+import com.jd.blockchain.crypto.smutils.asymmetric.SM2Utils;
+import com.jd.blockchain.crypto.smutils.hash.SM3Utils;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.encoders.Hex;
+
+import java.math.BigInteger;
+
+public class MultiSum {
+
+ private ECPrivateKeyParameters ePrivKey;
+ private ECPublicKeyParameters ePubKey;
+ private ECCurve curve;
+ private ECDomainParameters domainParams;
+
+ public void generateEphemeralKeyPair(){
+ AsymmetricCipherKeyPair eKeyPair = SM2Utils.generateKeyPair();
+ this.ePrivKey = (ECPrivateKeyParameters) eKeyPair.getPrivate();
+ this.ePubKey = (ECPublicKeyParameters) eKeyPair.getPublic();
+ this.curve = SM2Utils.getCurve();
+ this.domainParams = SM2Utils.getDomainParams();
+
+ }
+
+ public BigInteger calculateAgreement(CipherParameters otherEPubKey){
+ ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement();
+ basicAgreement.init(ePrivKey);
+ return basicAgreement.calculateAgreement(otherEPubKey);
+ }
+
+ public static BigInteger deriveShares(byte[] frontID, byte[] rearID, BigInteger agreement){
+ byte[] agreementBytes = agreement.toByteArray();
+ byte[] inputBytes = BytesUtils.concat(frontID,rearID,agreementBytes);
+ return new BigInteger(1,SM3Utils.hash(inputBytes));
+ }
+
+ public static BigInteger encryptBlindedMsg(PublicKey encKey, BigInteger msg, BigInteger frontShare, BigInteger rearShare){
+ return encKey.encrypt(msg.add(frontShare).subtract(rearShare).mod(encKey.getN()));
+ }
+
+ public static BigInteger aggregateCiphertexts(PublicKey encKey, BigInteger... bigIntegersList){
+ BigInteger aggregatedCiphertext = BigInteger.ONE;
+ for (BigInteger entry : bigIntegersList) {
+ aggregatedCiphertext = aggregatedCiphertext.multiply(entry).mod(encKey.getnSquared());
+ }
+ return aggregatedCiphertext;
+ }
+
+ public static BigInteger decrypt(KeyPair keyPair, BigInteger ciphertext){
+ return keyPair.decrypt(ciphertext);
+ }
+
+ public ECPublicKeyParameters getEPubKey(){return ePubKey;}
+
+ public ECPrivateKeyParameters getEPrivKey(){return ePrivKey;}
+
+
+ public byte[] getEPubKeyBytes(){
+ byte[] ePubKeyBytes = new byte[65];
+ byte[] ePubKeyBytesX = ePubKey.getQ().getAffineXCoord().getEncoded();
+ byte[] ePubKeyBytesY = ePubKey.getQ().getAffineYCoord().getEncoded();
+ System.arraycopy(Hex.decode("04"),0,ePubKeyBytes,0,1);
+ System.arraycopy(ePubKeyBytesX,0,ePubKeyBytes,1,32);
+ System.arraycopy(ePubKeyBytesY,0,ePubKeyBytes,1+32,32);
+ return ePubKeyBytes;
+ }
+
+ public byte[] getEPrivKeyBytes(){
+ return PaillierUtils.BigIntegerToLBytes(ePrivKey.getD(),32);
+ }
+
+ public ECPublicKeyParameters resolveEPubKey(byte[] ePubKeyBytes){
+ byte[] ePubKeyX = new byte[32];
+ byte[] ePubKeyY = new byte[32];
+ System.arraycopy(ePubKeyBytes,1,ePubKeyX,0,32);
+ System.arraycopy(ePubKeyBytes,1+32,ePubKeyY,0,32);
+ ECPoint ePubKeyPoint = curve.createPoint(new BigInteger(1,ePubKeyX), new BigInteger(1,ePubKeyY));
+ return new ECPublicKeyParameters(ePubKeyPoint,domainParams);
+ }
+
+ public ECPrivateKeyParameters resolveEPrivKey(byte[] ePrivKeyBytes){
+ return new ECPrivateKeyParameters(new BigInteger(1,ePrivKeyBytes),domainParams);
+ }
+
+}
diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/KeyPair.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/KeyPair.java
new file mode 100644
index 00000000..68fd6970
--- /dev/null
+++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/KeyPair.java
@@ -0,0 +1,98 @@
+package com.jd.blockchain.crypto.paillier;
+
+import java.math.BigInteger;
+import java.util.List;
+
+import com.jd.blockchain.utils.io.BytesUtils;
+
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Hendrik Kunert
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * A class that holds a pair of associated public and private keys.
+ */
+public class KeyPair {
+ private final PrivateKey privateKey;
+ private final PublicKey publicKey;
+ private final BigInteger upperBound;
+
+ public KeyPair(PrivateKey privateKey, PublicKey publicKey, BigInteger upperBound) {
+ this.privateKey = privateKey;
+ this.publicKey = publicKey;
+ this.upperBound = upperBound;
+ }
+
+ public KeyPair(byte[] privKeyBytes,byte[] pubKeyBytes,byte[] upperBoundBytes){
+ this.privateKey = new PrivateKey(privKeyBytes);
+ this.publicKey = new PublicKey(pubKeyBytes);
+ this.upperBound = new BigInteger(upperBoundBytes);
+ }
+
+ public KeyPair(byte[] keyPairBytes){
+ List list = PaillierUtils.split(keyPairBytes, "##KeyPair##".getBytes());
+ this.privateKey = new PrivateKey(list.get(0));
+ this.publicKey = new PublicKey(list.get(1));
+ this.upperBound = new BigInteger(list.get(2));
+ }
+
+ public PrivateKey getPrivateKey() {
+ return privateKey;
+ }
+
+ public PublicKey getPublicKey() {
+ return publicKey;
+ }
+
+ public BigInteger getUpperBound() { return upperBound; }
+
+ /**
+ * Decrypts the given ciphertext.
+ *
+ * @param c The ciphertext that should be decrypted.
+ * @return The corresponding plaintext. If an upper bound was given to {@link KeyPairBuilder},
+ * the result can also be negative. See {@link KeyPairBuilder#upperBound(BigInteger)} for details.
+ */
+ public final BigInteger decrypt(BigInteger c) {
+
+ BigInteger n = publicKey.getN();
+ BigInteger nSquare = publicKey.getnSquared();
+ BigInteger lambda = privateKey.getLambda();
+
+ BigInteger u = privateKey.getPreCalculatedDenominator();
+
+ BigInteger p = c.modPow(lambda, nSquare).subtract(BigInteger.ONE).divide(n).multiply(u).mod(n);
+
+ if (upperBound != null && p.compareTo(upperBound) > 0) {
+ p = p.subtract(n);
+ }
+
+ return p;
+ }
+
+ public byte[] getUpperBoundBytes(){ return upperBound.toByteArray(); }
+
+ public byte[] getKeyPairBytes(){
+ return BytesUtils.concat(privateKey.getPrivKeyBytes(),"##KeyPair##".getBytes(),publicKey.getPubKeyBytes(),"##KeyPair##".getBytes(),upperBound.toByteArray());
+ }
+}
diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/KeyPairBuilder.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/KeyPairBuilder.java
new file mode 100644
index 00000000..c4234cae
--- /dev/null
+++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/KeyPairBuilder.java
@@ -0,0 +1,165 @@
+package com.jd.blockchain.crypto.paillier;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Random;
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Hendrik Kunert
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * A class that is used for generating a pair of associated public and private
+ * keys.
+ *
+ * @see KeyPair
+ */
+public class KeyPairBuilder {
+
+ private int bits = 1024;
+
+ private int certainty = 0;
+
+ private Random rng;
+
+ private BigInteger upperBound;
+
+ /**
+ * Sets the size of the key to be created.
+ *
+ * The default size is 1024 bits.
+ *
+ * @param bits The size of the key in bits.
+ * @return This instance of KeyPairBuilder for method chaining.
+ */
+ public KeyPairBuilder bits(int bits) {
+ this.bits = bits;
+ return this;
+ }
+
+ /**
+ * See {@link BigInteger#BigInteger(int, int, Random)} for more details.
+ *
+ * The default value is 0.
+ *
+ * @return This instance of KeyPairBuilder for method chaining.
+ */
+ public KeyPairBuilder certainty(int certainty) {
+ this.certainty = certainty;
+ return this;
+ }
+
+ /**
+ * Sets the random number generator that is used for the generation of
+ * internally needed prime numbers.
+ *
+ * The default is {@link SecureRandom}.
+ *
+ * Warning:
+ * The change of this value affects the security of the whole cryptographic
+ * system.
+ *
+ * @param rng The random number generator that should be used instead of
+ * {@link SecureRandom}.
+ * @return This instance of KeyPairBuilder for method chaining.
+ */
+ public KeyPairBuilder randomNumberGenerator(Random rng) {
+ this.rng = rng;
+ return this;
+ }
+
+ /**
+ * Sets an upper bound that is used for decrypting ciphertexts representing a negative value.
+ *
+ * In most cases the upper bound should be the same as of the underlying number system -
+ * for example {@link Integer#MAX_VALUE}.
+ *
+ * @param b The upper bound.
+ * @return This instance of KeyPairBuilder for method chaining.
+ */
+ public KeyPairBuilder upperBound(BigInteger b) {
+ this.upperBound = b;
+ return this;
+ }
+
+ /**
+ * Creates a pair of associated public and private keys.
+ *
+ * @return The pair of associated public and private keys.
+ */
+ public KeyPair generateKeyPair() {
+ if (rng == null) {
+ rng = new SecureRandom();
+ }
+
+ BigInteger p, q;
+ int length = bits / 2;
+ if (certainty > 0) {
+ p = new BigInteger(length, certainty, rng);
+ q = new BigInteger(length, certainty, rng);
+ } else {
+ p = BigInteger.probablePrime(length, rng);
+ q = BigInteger.probablePrime(length, rng);
+ }
+
+ BigInteger n = p.multiply(q);
+ BigInteger nSquared = n.multiply(n);
+
+ BigInteger pMinusOne = p.subtract(BigInteger.ONE);
+ BigInteger qMinusOne = q.subtract(BigInteger.ONE);
+
+ BigInteger lambda = this.lcm(pMinusOne, qMinusOne);
+
+ BigInteger g;
+ BigInteger helper;
+
+ do {
+ g = new BigInteger(bits, rng);
+ helper = calculateL(g.modPow(lambda, nSquared), n);
+
+ } while (!helper.gcd(n).equals(BigInteger.ONE));
+
+ PublicKey publicKey = new PublicKey(n, nSquared, g, bits);
+ PrivateKey privateKey = new PrivateKey(lambda, helper.modInverse(n));
+
+ return new KeyPair(privateKey, publicKey, upperBound);
+
+ }
+
+ // TODO separate this somewhere
+ private BigInteger calculateL(BigInteger u, BigInteger n) {
+ BigInteger result = u.subtract(BigInteger.ONE);
+ result = result.divide(n);
+ return result;
+ }
+
+ // TODO add to own BigInteger extended class
+ private BigInteger lcm(BigInteger a, BigInteger b) {
+ BigInteger result;
+ BigInteger gcd = a.gcd(b);
+
+ result = a.abs().divide(gcd);
+ result = result.multiply(b.abs());
+
+ return result;
+ }
+}
diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierUtils.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierUtils.java
new file mode 100644
index 00000000..ac14f22b
--- /dev/null
+++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierUtils.java
@@ -0,0 +1,58 @@
+package com.jd.blockchain.crypto.paillier;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+public class PaillierUtils {
+
+ // To convert BigInteger to byte[] whose length is l
+ public static byte[] BigIntegerToLBytes(BigInteger b, int l){
+ byte[] tmp = b.toByteArray();
+ byte[] result = new byte[l];
+ if (tmp.length > result.length)
+ System.arraycopy(tmp, tmp.length-result.length, result, 0, result.length);
+ else System.arraycopy(tmp,0,result,result.length-tmp.length,tmp.length);
+ return result;
+ }
+
+ public static byte[] intToBytes(int i){
+ byte[] result = new byte[4];
+ result[0] = (byte) (i >> 24);
+ result[1] = (byte) (i >> 16);
+ result[2] = (byte) (i >> 8);
+ result[3] = (byte) (i);
+ return result;
+ }
+
+ public static int bytesToInt(byte[] array){
+ int result = 0;
+ result |= ((array[0] & 0xFF) << 24);
+ result |= ((array[1] & 0xFF) << 16);
+ result |= ((array[2] & 0xFF) << 8);
+ result |= ((array[3] & 0xFF));
+ return result;
+ }
+
+ public static List split(byte[] array, byte[] delimiter) {
+ List byteArrays = new LinkedList<>();
+ if (delimiter.length == 0) {
+ return byteArrays;
+ }
+ int begin = 0;
+
+ outer:
+ for (int i = 0; i < array.length - delimiter.length + 1; i++) {
+ for (int j = 0; j < delimiter.length; j++) {
+ if (array[i + j] != delimiter[j]) {
+ continue outer;
+ }
+ }
+ byteArrays.add(Arrays.copyOfRange(array, begin, i));
+ begin = i + delimiter.length;
+ }
+ byteArrays.add(Arrays.copyOfRange(array, begin, array.length));
+ return byteArrays;
+ }
+}
diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PrivateKey.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PrivateKey.java
new file mode 100644
index 00000000..ac4b656a
--- /dev/null
+++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PrivateKey.java
@@ -0,0 +1,75 @@
+package com.jd.blockchain.crypto.paillier;
+
+import java.math.BigInteger;
+import java.util.List;
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Hendrik Kunert
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+import com.jd.blockchain.utils.io.BytesUtils;
+
+/**
+ * A class that represents the private part of the Paillier key pair.
+ */
+public class PrivateKey {
+
+ private final BigInteger lambda;
+ private final BigInteger preCalculatedDenominator;
+
+ public PrivateKey(BigInteger lambda, BigInteger preCalculatedDenominator) {
+ this.lambda = lambda;
+
+ this.preCalculatedDenominator = preCalculatedDenominator;
+ }
+
+ public PrivateKey(byte[] lambdaBytes, byte[] preCalculatedDenominatorBytes){
+ this.lambda = new BigInteger(lambdaBytes);
+ this.preCalculatedDenominator = new BigInteger(preCalculatedDenominatorBytes);
+ }
+
+ public PrivateKey(byte[] privKeyBytes){
+ List list = PaillierUtils.split(privKeyBytes, "##PrivateKey##".getBytes());
+ this.lambda = new BigInteger(list.get(0));
+ this.preCalculatedDenominator = new BigInteger(list.get(1));
+ }
+
+ public BigInteger getLambda() {
+ return lambda;
+ }
+
+ public BigInteger getPreCalculatedDenominator() {
+ return preCalculatedDenominator;
+ }
+
+ public byte[] getLambdaBytes(){
+ return lambda.toByteArray();
+ }
+
+ public byte[] getPreCalculatedDenominatorBytes(){
+ return preCalculatedDenominator.toByteArray();
+ }
+
+ public byte[] getPrivKeyBytes(){
+ return BytesUtils.concat(getLambdaBytes(),"##PrivateKey##".getBytes(),getPreCalculatedDenominatorBytes());
+ }
+}
diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PublicKey.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PublicKey.java
new file mode 100644
index 00000000..aa1d3f51
--- /dev/null
+++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PublicKey.java
@@ -0,0 +1,129 @@
+package com.jd.blockchain.crypto.paillier;
+
+import java.math.BigInteger;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Hendrik Kunert
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+import com.jd.blockchain.utils.io.ByteArray;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+/**
+ * A class that represents the public part of the Paillier key pair.
+ *
+ * As in all asymmetric cryptographic systems it is responsible for the
+ * encryption.
+ *
+ * Additional instructions for the decryption can be found on {@link KeyPair}.
+ *
+ * @see KeyPair
+ */
+public class PublicKey {
+ private final int bits;
+ private final BigInteger n;
+ private final BigInteger nSquared;
+ private final BigInteger g;
+
+ public PublicKey(BigInteger n, BigInteger nSquared, BigInteger g, int bits) {
+ this.n = n;
+ this.nSquared = nSquared;
+ this.bits = bits;
+ this.g = g;
+ }
+
+ public PublicKey(byte[] nBytes, byte[] nSquaredBytes, byte[] gBytes, byte[] bitsBytes) {
+ this.n = new BigInteger(nBytes);
+ this.nSquared = new BigInteger(nSquaredBytes);
+ this.g = new BigInteger(gBytes);
+ this.bits = PaillierUtils.bytesToInt(bitsBytes);
+ }
+
+ public PublicKey(byte[] pubKeyBytes){
+ List list = PaillierUtils.split(pubKeyBytes, "##PublicKey##".getBytes());
+ this.n = new BigInteger(list.get(0));
+ this.nSquared = new BigInteger(list.get(1));
+ this.g = new BigInteger(list.get(2));
+ this.bits = PaillierUtils.bytesToInt(list.get(3));
+ }
+
+
+ public int getBits() {
+ return bits;
+ }
+
+ public BigInteger getN() {
+ return n;
+ }
+
+ public BigInteger getnSquared() {
+ return nSquared;
+ }
+
+ public BigInteger getG() {
+ return g;
+ }
+
+ /**
+ * Encrypts the given plaintext.
+ *
+ * @param m The plaintext that should be encrypted.
+ * @return The corresponding ciphertext.
+ */
+ public final BigInteger encrypt(BigInteger m) {
+
+ BigInteger r;
+ do {
+ r = new BigInteger(bits, new Random());
+ } while (r.compareTo(n) >= 0);
+
+ BigInteger result = g.modPow(m, nSquared);
+ BigInteger x = r.modPow(n, nSquared);
+
+ result = result.multiply(x);
+ result = result.mod(nSquared);
+
+ return result;
+ }
+
+ public byte[] getBitsBytes(){
+ return PaillierUtils.intToBytes(bits);
+ }
+
+ public byte[] getNBytes(){ return n.toByteArray(); }
+
+ public byte[] getNSquaredBytes(){
+ return nSquared.toByteArray();
+ }
+
+ public byte[] getGBytes(){
+ return g.toByteArray();
+ }
+
+ public byte[] getPubKeyBytes(){
+ return BytesUtils.concat(getNBytes(),"##PublicKey##".getBytes(),getNSquaredBytes(),"##PublicKey##".getBytes(),getGBytes(),"##PublicKey##".getBytes(),getBitsBytes());
+ }
+}
+
diff --git a/source/crypto/crypto-adv/src/main/resources/libsodium.23.dylib b/source/crypto/crypto-adv/src/main/resources/libsodium.23.dylib
new file mode 100755
index 00000000..bce89d3f
Binary files /dev/null and b/source/crypto/crypto-adv/src/main/resources/libsodium.23.dylib differ
diff --git a/source/crypto/crypto-adv/src/main/resources/libsodium.so.23.1.0 b/source/crypto/crypto-adv/src/main/resources/libsodium.so.23.1.0
new file mode 100755
index 00000000..44c1ef1a
Binary files /dev/null and b/source/crypto/crypto-adv/src/main/resources/libsodium.so.23.1.0 differ
diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/ecvrf/VRFTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/ecvrf/VRFTest.java
new file mode 100644
index 00000000..5c109651
--- /dev/null
+++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/ecvrf/VRFTest.java
@@ -0,0 +1,73 @@
+package test.com.jd.blockchain.crypto.ecvrf;
+
+import com.jd.blockchain.crypto.ecvrf.VRF;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class VRFTest {
+
+ @Test
+ public void testVRF() {
+
+ byte[] msg = "你好".getBytes();
+
+ //初始化一个异常
+ Exception actualEx = null;
+
+ try{
+ VRF.getLib();
+ long skGenStartTime = System.currentTimeMillis();
+ byte[] sk = VRF.genSecretKey();
+ long skGenTime = System.currentTimeMillis() - skGenStartTime;
+ System.out.println(String.format("VRF sk generation time = %s ms", skGenTime));
+
+ long pkGenStartTime = System.currentTimeMillis();
+ byte[] pk = VRF.sk2pk(sk);
+ long pkGenTime = System.currentTimeMillis() - pkGenStartTime;
+ System.out.println(String.format("VRF pk generation time = %s ms", pkGenTime));
+
+ long proofGenStartTime = System.currentTimeMillis();
+ byte[] proof = VRF.prove(sk, msg);
+ long proofGenTime = System.currentTimeMillis() - proofGenStartTime;
+ System.out.println(String.format("VRF proof generation time = %s ms", proofGenTime));
+
+ long pkValidationStartTime = System.currentTimeMillis();
+ assertTrue(VRF.IsValidPk(pk));
+ long pkValidationTime = System.currentTimeMillis() - pkValidationStartTime;
+ System.out.println(String.format("VRF pk validation time = %s ms", pkValidationTime));
+
+ assertNotNull(proof);
+
+ long proof2hashStartTime = System.currentTimeMillis();
+ byte[] output = VRF.proof2hash(proof);
+ long proof2hashTime = System.currentTimeMillis() - proof2hashStartTime;
+ System.out.println(String.format("VRF proof2hash time = %s ms", proof2hashTime));
+
+ long verificationStartTime = System.currentTimeMillis();
+ boolean isValid = VRF.verify(pk, proof, msg);
+ long verificationTime = System.currentTimeMillis() - verificationStartTime;
+ System.out.println(String.format("VRF verification time = %s ms", verificationTime));
+
+ assertTrue(isValid);
+ assertNotNull(output);
+
+ }catch (Exception e){
+ actualEx = e;
+ }
+
+ String osName = System.getProperty("os.name").toLowerCase();
+
+ if (osName.startsWith("mac")
+ || osName.contains("linux")) {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ Class> expectedException = IllegalArgumentException.class;
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+}
diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/mpc/MultiSumTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/mpc/MultiSumTest.java
new file mode 100644
index 00000000..4dff44cc
--- /dev/null
+++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/mpc/MultiSumTest.java
@@ -0,0 +1,105 @@
+package test.com.jd.blockchain.crypto.mpc;
+
+import com.jd.blockchain.crypto.mpc.MultiSum;
+import com.jd.blockchain.crypto.paillier.KeyPair;
+import com.jd.blockchain.crypto.paillier.KeyPairBuilder;
+import com.jd.blockchain.crypto.paillier.PublicKey;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.math.BigInteger;
+
+import static org.junit.Assert.*;
+
+public class MultiSumTest {
+
+ private KeyPair keyPair;
+ private PublicKey encKey;
+
+ @Before
+ public void init() {
+ KeyPairBuilder keygen = new KeyPairBuilder();
+ keyPair = keygen.generateKeyPair();
+ encKey = keyPair.getPublicKey();
+ }
+
+ @Test
+ public void testMultiSum() {
+
+ MultiSum instance1 = new MultiSum();
+ MultiSum instance2 = new MultiSum();
+ MultiSum instance3 = new MultiSum();
+
+ BigInteger value1 = BigInteger.valueOf(6);
+ BigInteger value2 = BigInteger.valueOf(60);
+ BigInteger value3 = BigInteger.valueOf(600);
+ BigInteger expectedSum = BigInteger.valueOf(666);
+
+ byte[] id1 = "1".getBytes();
+ byte[] id2 = "2".getBytes();
+ byte[] id3 = "3".getBytes();
+
+ instance1.generateEphemeralKeyPair();
+ instance2.generateEphemeralKeyPair();
+ instance3.generateEphemeralKeyPair();
+
+ ECPublicKeyParameters ePubKey1 = instance1.getEPubKey();
+ ECPublicKeyParameters ePubKey2 = instance2.getEPubKey();
+ ECPublicKeyParameters ePubKey3 = instance3.getEPubKey();
+
+ BigInteger sk12 = instance1.calculateAgreement(ePubKey2);
+ BigInteger sk23 = instance2.calculateAgreement(ePubKey3);
+ BigInteger sk31 = instance1.calculateAgreement(ePubKey3);
+
+ assertEquals(sk12,instance2.calculateAgreement(ePubKey1));
+ assertEquals(sk23,instance3.calculateAgreement(ePubKey2));
+ assertEquals(sk31,instance3.calculateAgreement(ePubKey1));
+
+ BigInteger s12 = MultiSum.deriveShares(id1,id2,sk12);
+ BigInteger s23 = MultiSum.deriveShares(id2,id3,sk23);
+ BigInteger s31 = MultiSum.deriveShares(id3,id1,sk31);
+
+ assertEquals(s12, MultiSum.deriveShares(id1,id2,sk12));
+ assertEquals(s23, MultiSum.deriveShares(id2,id3,sk23));
+ assertEquals(s31, MultiSum.deriveShares(id3,id1,sk31));
+
+ BigInteger c1 = MultiSum.encryptBlindedMsg(encKey,value1,s12,s31);
+ BigInteger c2 = MultiSum.encryptBlindedMsg(encKey,value2,s23,s12);
+ BigInteger c3 = MultiSum.encryptBlindedMsg(encKey,value3,s31,s23);
+
+ BigInteger aggregatedCiphertext = MultiSum.aggregateCiphertexts(encKey,c1,c2,c3);
+
+ BigInteger decryptedValue = MultiSum.decrypt(keyPair,aggregatedCiphertext);
+
+ assertEquals(expectedSum,decryptedValue);
+ }
+
+ @Test
+ public void testResolveEPrivKey(){
+
+ MultiSum instance = new MultiSum();
+ instance.generateEphemeralKeyPair();
+
+ ECPrivateKeyParameters expectedEPrivKey = instance.getEPrivKey();
+ byte[] ePrivKeyBytes = instance.getEPrivKeyBytes();
+ ECPrivateKeyParameters ePrivKey = instance.resolveEPrivKey(ePrivKeyBytes);
+ assertEquals(expectedEPrivKey.getD(),ePrivKey.getD());
+ }
+
+ @Test
+ public void testResolveEPubKey(){
+
+ MultiSum instance = new MultiSum();
+ instance.generateEphemeralKeyPair();
+
+ ECPublicKeyParameters expectedEPubKey = instance.getEPubKey();
+ byte[] ePubKeyBytes = instance.getEPubKeyBytes();
+ ECPublicKeyParameters ePubKey = instance.resolveEPubKey(ePubKeyBytes);
+
+ assertEquals(Hex.toHexString(expectedEPubKey.getQ().getAffineXCoord().getEncoded()),Hex.toHexString(ePubKey.getQ().getAffineXCoord().getEncoded()));
+ assertEquals(Hex.toHexString(expectedEPubKey.getQ().getAffineYCoord().getEncoded()),Hex.toHexString(ePubKey.getQ().getAffineYCoord().getEncoded()));
+ }
+}
\ No newline at end of file
diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/DecryptionTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/DecryptionTest.java
new file mode 100644
index 00000000..8994d9dc
--- /dev/null
+++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/DecryptionTest.java
@@ -0,0 +1,58 @@
+package test.com.jd.blockchain.crypto.paillier;
+
+import com.jd.blockchain.crypto.paillier.KeyPair;
+import com.jd.blockchain.crypto.paillier.KeyPairBuilder;
+import com.jd.blockchain.crypto.paillier.PublicKey;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Created by kunerd on 22.09.15.
+ */
+@RunWith(value = Parameterized.class)
+public class DecryptionTest {
+
+ @Parameterized.Parameters
+ public static Collection data() {
+ return Arrays.asList(createTestParameter(Long.MIN_VALUE),
+ createTestParameter(Integer.MIN_VALUE),
+ createTestParameter(Short.MIN_VALUE),
+ createTestParameter(0),
+ createTestParameter(Short.MAX_VALUE),
+ createTestParameter(Integer.MAX_VALUE),
+ createTestParameter(Long.MAX_VALUE));
+ }
+
+ private BigInteger input;
+ private BigInteger expected;
+
+ public DecryptionTest(BigInteger input, BigInteger expected) {
+ this.input = input;
+ this.expected = expected;
+ }
+
+ @Test
+ public void test() {
+ KeyPair keyPair = new KeyPairBuilder().upperBound(BigInteger.valueOf(Long.MAX_VALUE))
+ .generateKeyPair();
+ PublicKey publicKey = keyPair.getPublicKey();
+
+ BigInteger encryptedData = publicKey.encrypt(input);
+
+ assertEquals(expected, keyPair.decrypt(encryptedData));
+ }
+
+ private static Object[] createTestParameter(long plaintext) {
+ BigInteger p = BigInteger.valueOf(plaintext);
+ return new Object[]{p, p};
+ }
+
+}
+
diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/HomomorphicPropertiesTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/HomomorphicPropertiesTest.java
new file mode 100644
index 00000000..2b156386
--- /dev/null
+++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/HomomorphicPropertiesTest.java
@@ -0,0 +1,90 @@
+package test.com.jd.blockchain.crypto.paillier;
+
+import com.jd.blockchain.crypto.paillier.KeyPair;
+import com.jd.blockchain.crypto.paillier.KeyPairBuilder;
+import com.jd.blockchain.crypto.paillier.PublicKey;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.math.BigInteger;
+
+import static org.junit.Assert.assertEquals;
+
+public class HomomorphicPropertiesTest {
+ private KeyPair keypair;
+ private PublicKey publicKey;
+
+ @Before
+ public void init() {
+ KeyPairBuilder keygen = new KeyPairBuilder();
+ this.keypair = keygen.generateKeyPair();
+ this.publicKey = keypair.getPublicKey();
+ }
+
+ @Test
+ public void testHomomorphicAddition() {
+ BigInteger plainA = BigInteger.valueOf(102);
+ BigInteger plainB = BigInteger.valueOf(203);
+
+ BigInteger encryptedA = publicKey.encrypt(plainA);
+ BigInteger encryptedB = publicKey.encrypt(plainB);
+
+ BigInteger decryptedProduct = keypair.decrypt(encryptedA.multiply(
+ encryptedB).mod(publicKey.getnSquared()));
+ BigInteger plainSum = plainA.add(plainB).mod(publicKey.getN());
+
+ assertEquals(decryptedProduct, plainSum);
+ }
+
+ @Test
+ public void testHomomorphicConstantMultiplication() {
+ BigInteger plainA = BigInteger.valueOf(14);
+ BigInteger plainB = BigInteger.valueOf(203);
+
+ BigInteger encryptedA = publicKey.encrypt(plainA);
+
+ BigInteger decryptedPow = keypair.decrypt(encryptedA.modPow(plainB,
+ publicKey.getnSquared()));
+ BigInteger plainSum = plainA.multiply(plainB).mod(publicKey.getN());
+
+ assertEquals(decryptedPow, plainSum);
+ }
+
+ @Test
+ public void testHomomorphicMultiplication() {
+ BigInteger plainA = BigInteger.valueOf(23);
+ BigInteger plainB = BigInteger.valueOf(234);
+
+ BigInteger encryptedA = publicKey.encrypt(plainA);
+ BigInteger decryptedPowA = keypair.decrypt(encryptedA.modPow(
+ plainB, publicKey.getnSquared()));
+ BigInteger plainSumA = plainA.multiply(plainB).mod(publicKey.getN());
+
+ assertEquals(decryptedPowA, plainSumA);
+
+ BigInteger encryptedB = publicKey.encrypt(plainB);
+ BigInteger decryptedPowB = keypair.decrypt(encryptedB.modPow(
+ plainA, publicKey.getnSquared()));
+ BigInteger plainSumB = plainA.multiply(plainB).mod(publicKey.getN());
+
+ assertEquals(decryptedPowB, plainSumB);
+
+ assertEquals(decryptedPowA, decryptedPowB);
+ }
+
+ @Test
+ public void testHomomorphicMultiplicationPowG() {
+ BigInteger plainA = BigInteger.valueOf(230);
+ BigInteger plainB = BigInteger.valueOf(100);
+
+ BigInteger g = publicKey.getG();
+
+ BigInteger encryptedA = publicKey.encrypt(plainA);
+ BigInteger decryptedPow = keypair.decrypt(encryptedA.multiply(g.modPow(
+ plainB, publicKey.getnSquared()).mod(publicKey.getnSquared())));
+
+ BigInteger plainSumA = plainA.add(plainB).mod(publicKey.getN());
+
+ assertEquals(decryptedPow, plainSumA);
+ }
+}
diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/JPaillierTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/JPaillierTest.java
new file mode 100644
index 00000000..12e0258d
--- /dev/null
+++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/JPaillierTest.java
@@ -0,0 +1,43 @@
+package test.com.jd.blockchain.crypto.paillier;
+
+import com.jd.blockchain.crypto.paillier.KeyPair;
+import com.jd.blockchain.crypto.paillier.KeyPairBuilder;
+import com.jd.blockchain.crypto.paillier.PublicKey;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.math.BigInteger;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+public class JPaillierTest {
+ private KeyPair keyPair;
+ private PublicKey publicKey;
+
+ @Before
+ public void init() {
+ KeyPairBuilder keygen = new KeyPairBuilder();
+ keyPair = keygen.generateKeyPair();
+ publicKey = keyPair.getPublicKey();
+ }
+
+ @Test
+ public void testEncryption() {
+ BigInteger plainData = BigInteger.valueOf(10);
+
+ BigInteger encryptedData = publicKey.encrypt(plainData);
+
+ assertNotEquals(plainData, encryptedData);
+ }
+
+ @Test
+ public void testDecyption() {
+ BigInteger plainData = BigInteger.valueOf(10);
+
+ BigInteger encryptedData = publicKey.encrypt(plainData);
+ BigInteger decryptedData = keyPair.decrypt(encryptedData);
+
+ assertEquals(plainData, decryptedData);
+ }
+}
diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderPrivateKeyTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderPrivateKeyTest.java
new file mode 100644
index 00000000..ac64efda
--- /dev/null
+++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderPrivateKeyTest.java
@@ -0,0 +1,44 @@
+package test.com.jd.blockchain.crypto.paillier;
+
+import com.jd.blockchain.crypto.paillier.KeyPair;
+import com.jd.blockchain.crypto.paillier.KeyPairBuilder;
+import com.jd.blockchain.crypto.paillier.PrivateKey;
+import com.jd.blockchain.crypto.paillier.PublicKey;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.math.BigInteger;
+
+import static org.junit.Assert.assertEquals;
+
+public class KeyPairBuilderPrivateKeyTest {
+ private KeyPairBuilder keygen;
+ private KeyPair keypair;
+ private PrivateKey privateKey;
+
+ @Before
+ public void init() {
+ this.keygen = new KeyPairBuilder();
+ this.keypair = keygen.generateKeyPair();
+ this.privateKey = keypair.getPrivateKey();
+ }
+
+ @Test
+ public void testPreCalculatedDenominator() {
+ PublicKey publicKey = keypair.getPublicKey();
+
+ BigInteger preCalculatedDenominator = privateKey.getPreCalculatedDenominator();
+
+ BigInteger g = publicKey.getG();
+ BigInteger n = publicKey.getN();
+ BigInteger nSquared = publicKey.getnSquared();
+ BigInteger lambda = privateKey.getLambda();
+
+ BigInteger expected = g.modPow(lambda, nSquared);
+ expected = expected.subtract(BigInteger.ONE);
+ expected = expected.divide(n);
+ expected = expected.modInverse(n);
+
+ assertEquals(expected, preCalculatedDenominator);
+ }
+}
diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderPublicKeyTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderPublicKeyTest.java
new file mode 100644
index 00000000..5517c97f
--- /dev/null
+++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderPublicKeyTest.java
@@ -0,0 +1,57 @@
+package test.com.jd.blockchain.crypto.paillier;
+
+import com.jd.blockchain.crypto.paillier.KeyPair;
+import com.jd.blockchain.crypto.paillier.KeyPairBuilder;
+import com.jd.blockchain.crypto.paillier.PrivateKey;
+import com.jd.blockchain.crypto.paillier.PublicKey;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.math.BigInteger;
+
+import static org.junit.Assert.assertEquals;
+
+public class KeyPairBuilderPublicKeyTest {
+
+ private KeyPair keypair;
+ private PublicKey publicKey;
+
+ @Before
+ public void init() {
+ KeyPairBuilder keygen = new KeyPairBuilder();
+ this.keypair = keygen.generateKeyPair();
+ this.publicKey = keypair.getPublicKey();
+ }
+
+ @Test
+ public void testBitsSetup() {
+ int BITS = 1024;
+ assertEquals(BITS, publicKey.getBits());
+ }
+
+ @Test
+ public void testCalculationOfNSquared() {
+
+ BigInteger n = publicKey.getN();
+ BigInteger nSquared = n.multiply(n);
+
+ assertEquals(nSquared, publicKey.getnSquared());
+ }
+
+ @Test
+ public void testCalculationOfGOfG() {
+ PrivateKey privateKey = keypair.getPrivateKey();
+
+ BigInteger n = publicKey.getN();
+ BigInteger nSquared = publicKey.getnSquared();
+ BigInteger g = publicKey.getG();
+ BigInteger lambda = privateKey.getLambda();
+
+ BigInteger l = g.modPow(lambda, nSquared);
+ l = l.subtract(BigInteger.ONE);
+ l = l.divide(n);
+
+ assertEquals(BigInteger.ONE, l.gcd(n));
+ }
+
+}
diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderTest.java
new file mode 100644
index 00000000..02efb028
--- /dev/null
+++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderTest.java
@@ -0,0 +1,113 @@
+package test.com.jd.blockchain.crypto.paillier;
+
+import static org.junit.Assert.assertEquals;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Random;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import com.jd.blockchain.crypto.paillier.KeyPair;
+import com.jd.blockchain.crypto.paillier.KeyPairBuilder;
+import com.jd.blockchain.crypto.paillier.PrivateKey;
+import com.jd.blockchain.crypto.paillier.PublicKey;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(KeyPairBuilder.class)
+public class KeyPairBuilderTest {
+
+ private static final int BITS = 128;
+
+ private KeyPairBuilder keygen;
+ private PublicKey publicKey;
+ private PrivateKey privateKey;
+
+ private BigInteger p = BigInteger.valueOf(5);
+ private BigInteger q = BigInteger.valueOf(7);
+ private BigInteger g1 = BigInteger.valueOf(35);
+ private BigInteger g2 = BigInteger.valueOf(36);
+
+ private Random rng;
+
+ @Before
+ public void beforeEach() {
+ rng = PowerMockito.mock(SecureRandom.class);
+
+ keygen = new KeyPairBuilder()
+ .bits(BITS)
+ .randomNumberGenerator(rng);
+
+ PowerMockito.mockStatic(BigInteger.class);
+ }
+
+ private void prepareTest() throws Exception {
+
+ PowerMockito.when(BigInteger.probablePrime(BITS / 2, rng)).thenReturn(p, q);
+
+ PowerMockito.whenNew(BigInteger.class).withArguments(BITS, rng).thenReturn(g1, g2);
+
+ KeyPair keypair = keygen.generateKeyPair();
+
+ publicKey = keypair.getPublicKey();
+ privateKey = keypair.getPrivateKey();
+ }
+
+ @Test
+ public void computationOfN() throws Exception {
+ prepareTest();
+
+ BigInteger e = p.multiply(q);
+ BigInteger a = publicKey.getN();
+
+ assertEquals(e, a);
+ }
+
+
+ @Test
+ public void computationOfLambda() throws Exception {
+ BigInteger e = new BigInteger("12");
+
+ prepareTest();
+
+ BigInteger a = privateKey.getLambda();
+
+ assertEquals(e, a);
+ }
+
+ @Test
+ public void computationOfG() throws Exception {
+ prepareTest();
+
+ PowerMockito.verifyNew(BigInteger.class, Mockito.times(2)).withArguments(Mockito.eq(128), Mockito.any(Random.class));
+ }
+
+ @Test
+ public void withoutCertainty() throws Exception {
+ prepareTest();
+
+ PowerMockito.verifyStatic(Mockito.times(2));
+ BigInteger.probablePrime(BITS / 2, rng);
+
+ }
+
+ @Test
+ public void withCertainty() throws Exception {
+ int certainty = 6;
+
+ keygen.certainty(certainty);
+
+ PowerMockito.whenNew(BigInteger.class).withArguments(BITS / 2, certainty, rng).thenReturn(p, q);
+
+ prepareTest();
+
+ PowerMockito.verifyNew(BigInteger.class, Mockito.times(2)).withArguments(BITS / 2, certainty, rng);
+ }
+
+}
diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/ResolveTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/ResolveTest.java
new file mode 100644
index 00000000..05022dad
--- /dev/null
+++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/ResolveTest.java
@@ -0,0 +1,124 @@
+package test.com.jd.blockchain.crypto.paillier;
+
+import com.jd.blockchain.crypto.paillier.*;
+import org.junit.Test;
+
+import java.math.BigInteger;
+
+import static org.junit.Assert.assertEquals;
+
+public class ResolveTest {
+
+ @Test
+ public void testResolvePrivateKey() {
+ KeyPairBuilder keygen = new KeyPairBuilder();
+ KeyPair keyPair = keygen.generateKeyPair();
+
+ PrivateKey privKey = keyPair.getPrivateKey();
+ BigInteger lambda = privKey.getLambda();
+ BigInteger preCalculatedDenominator = privKey.getPreCalculatedDenominator();
+
+ byte[] privKeyBytes = privKey.getPrivKeyBytes();
+ byte[] lambdaBytes = privKey.getLambdaBytes();
+ byte[] preCalculatedDenominatorBytes = privKey.getPreCalculatedDenominatorBytes();
+
+ assertEquals(lambda,new BigInteger(lambdaBytes));
+ assertEquals(preCalculatedDenominator,new BigInteger(preCalculatedDenominatorBytes));
+
+ assertEquals(lambda,new PrivateKey(lambda,preCalculatedDenominator).getLambda());
+ assertEquals(preCalculatedDenominator,(new PrivateKey(lambda,preCalculatedDenominator)).getPreCalculatedDenominator());
+
+ assertEquals(lambda,(new PrivateKey(lambdaBytes,preCalculatedDenominatorBytes)).getLambda());
+ assertEquals(preCalculatedDenominator,(new PrivateKey(lambdaBytes,preCalculatedDenominatorBytes)).getPreCalculatedDenominator());
+
+ assertEquals(lambda,(new PrivateKey(privKeyBytes)).getLambda());
+ assertEquals(preCalculatedDenominator,(new PrivateKey(privKeyBytes)).getPreCalculatedDenominator());
+ }
+
+ @Test
+ public void testResolvePublicKey() {
+ KeyPairBuilder keygen = new KeyPairBuilder();
+ KeyPair keyPair = keygen.generateKeyPair();
+
+ PublicKey pubKey = keyPair.getPublicKey();
+ int bits = pubKey.getBits();
+ BigInteger n = pubKey.getN();
+ BigInteger nSquared = pubKey.getnSquared();
+ BigInteger g = pubKey.getG();
+
+ byte[] pubKeyBytes = pubKey.getPubKeyBytes();
+ byte[] bitsBytes = pubKey.getBitsBytes();
+ byte[] nBytes = pubKey.getNBytes();
+ byte[] nSquaredBytes = pubKey.getNSquaredBytes();
+ byte[] gBytes = pubKey.getGBytes();
+
+ assertEquals(bits,PaillierUtils.bytesToInt(bitsBytes));
+ assertEquals(n,new BigInteger(nBytes));
+ assertEquals(nSquared,new BigInteger(nSquaredBytes));
+ assertEquals(g,new BigInteger(gBytes));
+
+ assertEquals(bits,(new PublicKey(n,nSquared,g,bits)).getBits());
+ assertEquals(n,(new PublicKey(n,nSquared,g,bits)).getN());
+ assertEquals(nSquared,(new PublicKey(n,nSquared,g,bits)).getnSquared());
+ assertEquals(g,(new PublicKey(n,nSquared,g,bits)).getG());
+
+ assertEquals(bits,(new PublicKey(nBytes,nSquaredBytes,gBytes,bitsBytes)).getBits());
+ assertEquals(n,(new PublicKey(nBytes,nSquaredBytes,gBytes,bitsBytes)).getN());
+ assertEquals(nSquared,(new PublicKey(nBytes,nSquaredBytes,gBytes,bitsBytes)).getnSquared());
+ assertEquals(g,(new PublicKey(nBytes,nSquaredBytes,gBytes,bitsBytes)).getG());
+
+ assertEquals(bits,(new PublicKey(pubKeyBytes)).getBits());
+ assertEquals(n,(new PublicKey(pubKeyBytes)).getN());
+ assertEquals(nSquared,(new PublicKey(pubKeyBytes)).getnSquared());
+ assertEquals(g,(new PublicKey(pubKeyBytes)).getG());
+ }
+
+ @Test
+ public void testResolveKeyPair() {
+ KeyPairBuilder keygen = new KeyPairBuilder();
+ keygen.upperBound(new BigInteger(PaillierUtils.intToBytes(Integer.MAX_VALUE)));
+ KeyPair keyPair = keygen.generateKeyPair();
+
+ PrivateKey privKey = keyPair.getPrivateKey();
+ PublicKey pubKey = keyPair.getPublicKey();
+ BigInteger upperBound = keyPair.getUpperBound();
+
+ byte[] keyPairBytes = keyPair.getKeyPairBytes();
+ byte[] privKeyBytes = privKey.getPrivKeyBytes();
+ byte[] pubKeyBytes = pubKey.getPubKeyBytes();
+ byte[] upperBoundBytes = keyPair.getUpperBoundBytes();
+
+ assertEquals(upperBound,keyPair.getUpperBound());
+ assertEquals(privKey.getLambda(),keyPair.getPrivateKey().getLambda());
+ assertEquals(privKey.getPreCalculatedDenominator(),keyPair.getPrivateKey().getPreCalculatedDenominator());
+ assertEquals(pubKey.getBits(),keyPair.getPublicKey().getBits());
+ assertEquals(pubKey.getN(),keyPair.getPublicKey().getN());
+ assertEquals(pubKey.getnSquared(),keyPair.getPublicKey().getnSquared());
+ assertEquals(pubKey.getG(),keyPair.getPublicKey().getG());
+
+ assertEquals(upperBound,(new KeyPair(privKey,pubKey,upperBound).getUpperBound()));
+ assertEquals(privKey.getLambda(),(new KeyPair(privKey,pubKey,upperBound).getPrivateKey().getLambda()));
+ assertEquals(privKey.getPreCalculatedDenominator(),(new KeyPair(privKey,pubKey,upperBound).getPrivateKey().getPreCalculatedDenominator()));
+ assertEquals(pubKey.getBits(),(new KeyPair(privKey,pubKey,upperBound).getPublicKey().getBits()));
+ assertEquals(pubKey.getN(),(new KeyPair(privKey,pubKey,upperBound).getPublicKey().getN()));
+ assertEquals(pubKey.getnSquared(),(new KeyPair(privKey,pubKey,upperBound).getPublicKey().getnSquared()));
+ assertEquals(pubKey.getG(),(new KeyPair(privKey,pubKey,upperBound).getPublicKey().getG()));
+
+ assertEquals(upperBound,(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getUpperBound()));
+ assertEquals(privKey.getLambda(),(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getPrivateKey().getLambda()));
+ assertEquals(privKey.getPreCalculatedDenominator(),(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getPrivateKey().getPreCalculatedDenominator()));
+ assertEquals(pubKey.getBits(),(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getPublicKey().getBits()));
+ assertEquals(pubKey.getN(),(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getPublicKey().getN()));
+ assertEquals(pubKey.getnSquared(),(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getPublicKey().getnSquared()));
+ assertEquals(pubKey.getG(),(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getPublicKey().getG()));
+
+ assertEquals(upperBound,(new KeyPair(keyPairBytes).getUpperBound()));
+ assertEquals(privKey.getLambda(),(new KeyPair(keyPairBytes).getPrivateKey().getLambda()));
+ assertEquals(privKey.getPreCalculatedDenominator(),(new KeyPair(keyPairBytes).getPrivateKey().getPreCalculatedDenominator()));
+ assertEquals(pubKey.getBits(),(new KeyPair(keyPairBytes).getPublicKey().getBits()));
+ assertEquals(pubKey.getN(),(new KeyPair(keyPairBytes).getPublicKey().getN()));
+ assertEquals(pubKey.getnSquared(),(new KeyPair(keyPairBytes).getPublicKey().getnSquared()));
+ assertEquals(pubKey.getG(),(new KeyPair(keyPairBytes).getPublicKey().getG()));
+
+ }
+}
diff --git a/source/crypto/crypto-framework/pom.xml b/source/crypto/crypto-framework/pom.xml
new file mode 100644
index 00000000..7816a2b9
--- /dev/null
+++ b/source/crypto/crypto-framework/pom.xml
@@ -0,0 +1,34 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ crypto
+ 0.8.2.RELEASE
+
+ crypto-framework
+
+
+
+ com.jd.blockchain
+ base
+ ${project.version}
+
+
+ com.jd.blockchain
+ utils-common
+ ${project.version}
+
+
+ com.jd.blockchain
+ utils-serialize
+ ${project.version}
+
+
+ com.jd.blockchain
+ binary-proto
+ ${project.version}
+
+
+
\ No newline at end of file
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/AddressEncoding.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/AddressEncoding.java
new file mode 100644
index 00000000..bc4a08a8
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/AddressEncoding.java
@@ -0,0 +1,66 @@
+package com.jd.blockchain.crypto;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.io.BytesEncoding;
+import com.jd.blockchain.utils.io.BytesUtils;
+import com.jd.blockchain.utils.io.NumberMask;
+import com.jd.blockchain.utils.security.RipeMD160Utils;
+import com.jd.blockchain.utils.security.ShaUtils;
+
+public class AddressEncoding {
+
+ /**
+ * 将区块链地址写入到输出流;
+ *
+ * 现将地址按 Base58 解码为字节数组,并将字节数组以 {@link BytesEncoding} 的方式写入输出流;
+ *
+ * 如果指定的地址为 null,则仅写入空字节数组;注:此种情况下,输出流并不是完全没有写入,而是实际上会被写入一个表示内容长度为 0 的头部字节;
+ *
+ * @param address
+ * 要写入的区块链地址;
+ * @param out
+ * 输出流;
+ * @return 写入的地址的字节数;如果指定地址为 null,则返回值为写入的头部字节数;;
+ */
+ public static int writeAddress(Bytes address, OutputStream out) {
+ return address.writeTo(out);
+ }
+
+ /**
+ * 从流中读取区块链地址;
+ *
+ * @param in
+ * @return
+ * @throws IOException
+ */
+ public static Bytes readAddress(InputStream in) throws IOException {
+ byte[] bytesAddress = BytesEncoding.read(NumberMask.TINY, in);
+ if (bytesAddress.length == 0) {
+ return null;
+ }
+ return new Bytes(bytesAddress);
+ }
+
+ /**
+ * 从公钥生成地址;
+ *
+ * @param pubKey
+ * @return
+ */
+ public static Bytes generateAddress(PubKey pubKey) {
+ byte[] h1Bytes = ShaUtils.hash_256(pubKey.getRawKeyBytes());
+ byte[] h2Bytes = RipeMD160Utils.hash(h1Bytes);
+ byte[] xBytes = BytesUtils.concat(new byte[]{AddressVersion.V1.CODE, pubKey.getAlgorithm().CODE}, h2Bytes);
+ byte[] checksum = Arrays.copyOf(ShaUtils.hash_256(ShaUtils.hash_256(xBytes)), 4);
+ byte[] addressBytes = BytesUtils.concat(xBytes, checksum);
+
+ return new Bytes(addressBytes);
+ }
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/AddressVersion.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/AddressVersion.java
new file mode 100644
index 00000000..dff8877b
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/AddressVersion.java
@@ -0,0 +1,13 @@
+package com.jd.blockchain.crypto;
+
+public enum AddressVersion {
+
+ V1((byte) 0x91);
+
+ public final byte CODE;
+
+ AddressVersion(byte code) {
+ CODE = code;
+ }
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/Ciphertext.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/Ciphertext.java
new file mode 100644
index 00000000..98b136dc
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/Ciphertext.java
@@ -0,0 +1,18 @@
+package com.jd.blockchain.crypto;
+
+/**
+ * 密文;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface Ciphertext extends CryptoBytes {
+
+ /**
+ * 原始的密文数据;
+ *
+ * @return
+ */
+ byte[] getRawCiphertext();
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoAlgorithm.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoAlgorithm.java
new file mode 100644
index 00000000..33a9c562
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoAlgorithm.java
@@ -0,0 +1,150 @@
+package com.jd.blockchain.crypto;
+
+import com.jd.blockchain.base.data.TypeCodes;
+import com.jd.blockchain.binaryproto.EnumContract;
+import com.jd.blockchain.binaryproto.EnumField;
+import com.jd.blockchain.utils.ValueType;
+
+
+@EnumContract(code= TypeCodes.ENUM_TYPE_CRYPTO_ALGORITHM)
+public enum CryptoAlgorithm {
+
+ SHA256(CryptoAlgorithmType.HASH, (byte) 0x01, false, false),
+
+ RIPEMD160(CryptoAlgorithmType.HASH, (byte) 0x02, false, false),
+
+ SM3(CryptoAlgorithmType.HASH, (byte) 0x03, false, false),
+
+ JNISHA256(CryptoAlgorithmType.HASH, (byte) 0x04, false, false),
+
+ JNIRIPEMD160(CryptoAlgorithmType.HASH, (byte) 0x05, false, false),
+
+ // 非对称签名/加密算法;
+
+ /**
+ * RSA 签名算法;可签名,可加密;
+ */
+ RSA(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x01, true, true),
+
+ /**
+ * ED25519 签名算法;只用于签名,没有加密特性;
+ */
+ ED25519(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x02, true, false),
+
+ /**
+ * ECDSA 签名算法;只用于签名,没有加密特性;
+ */
+ ECDSA(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x03, true, false),
+
+ /**
+ * 国密 SM2 算法;可签名,可加密;
+ */
+ SM2(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x04, true, true),
+
+ /**
+ * JNIED25519 签名算法;只用于签名,没有加密特性;
+ */
+ JNIED25519(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x05, true, false),
+
+ // 对称加密;
+ /**
+ * AES 算法;可加密;
+ */
+ AES(CryptoAlgorithmType.SYMMETRIC, (byte) 0x01, false, true),
+
+ SM4(CryptoAlgorithmType.SYMMETRIC, (byte) 0x02, false, true),
+
+ // 随机性;
+ /**
+ * 随机数算法,待定;
+ */
+ JAVA_SECURE(CryptoAlgorithmType.RANDOM, (byte) 0x01, false, false);
+
+ /**
+ * 密码算法的代号;
+ * 注:只占16位;
+ */
+ @EnumField(type= ValueType.INT8)
+ public final byte CODE;
+
+ private final boolean signable;
+
+ private final boolean encryptable;
+
+ private CryptoAlgorithm(byte algType, byte algId, boolean signable, boolean encryptable) {
+ this.CODE = (byte) (algType | algId);
+ this.signable = signable;
+ this.encryptable = encryptable;
+ }
+
+ /**
+ * 是否属于摘要算法;
+ *
+ * @return
+ */
+ public boolean isHash() {
+ return (CODE & CryptoAlgorithmType.HASH) == CryptoAlgorithmType.HASH;
+ }
+
+ /**
+ * 是否属于非对称密码算法;
+ *
+ * @return
+ */
+ public boolean isAsymmetric() {
+ return (CODE & CryptoAlgorithmType.ASYMMETRIC) == CryptoAlgorithmType.ASYMMETRIC;
+ }
+
+ /**
+ * 是否属于对称密码算法;
+ *
+ * @return
+ */
+ public boolean isSymmetric() {
+ return (CODE & CryptoAlgorithmType.SYMMETRIC) == CryptoAlgorithmType.SYMMETRIC;
+ }
+
+ /**
+ * 是否属于随机数算法;
+ *
+ * @return
+ */
+ public boolean isRandom() {
+ return (CODE & CryptoAlgorithmType.RANDOM) == CryptoAlgorithmType.RANDOM;
+ }
+
+ /**
+ * 是否支持签名操作;
+ *
+ * @return
+ */
+ public boolean isSignable() {
+ return signable;
+ }
+
+ /**
+ * 是否支持加密操作;
+ *
+ * @return
+ */
+ public boolean isEncryptable() {
+ return encryptable;
+ }
+
+ /**
+ * 返回指定编码对应的枚举实例;
+ *
+ * 如果不存在,则返回 null;
+ *
+ * @param code
+ * @return
+ */
+ public static CryptoAlgorithm valueOf(byte code) {
+ for (CryptoAlgorithm alg : CryptoAlgorithm.values()) {
+ if (alg.CODE == code) {
+ return alg;
+ }
+ }
+ throw new IllegalArgumentException("CryptoAlgorithm doesn't support enum code[" + code + "]!");
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoAlgorithmType.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoAlgorithmType.java
new file mode 100644
index 00000000..10e38f77
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoAlgorithmType.java
@@ -0,0 +1,24 @@
+package com.jd.blockchain.crypto;
+
+public class CryptoAlgorithmType {
+ /**
+ * Hash 类算法的掩码;
+ */
+ public static final byte HASH = 0x10;
+
+ /**
+ * 非对称加密类算法的掩码;
+ */
+ public static final byte ASYMMETRIC = 0x20;
+
+ /**
+ * 对称加密类算法的掩码;
+ */
+ public static final byte SYMMETRIC = 0x30;
+
+ /**
+ * 随机数类算法的掩码;
+ */
+ public static final byte RANDOM = 0x40;
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoBytes.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoBytes.java
new file mode 100644
index 00000000..2da141ef
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoBytes.java
@@ -0,0 +1,34 @@
+package com.jd.blockchain.crypto;
+
+import com.jd.blockchain.utils.io.BytesSerializable;
+
+/**
+ * {@link CryptoBytes} 表示与特定密码算法相关的编码数据;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface CryptoBytes extends BytesSerializable {
+
+ /**
+ * 算法标识符的长度;
+ */
+ int ALGORYTHM_BYTES = 1;
+
+ /**
+ * 算法;
+ *
+ * @return
+ */
+ CryptoAlgorithm getAlgorithm();
+
+ /**
+ * 返回编码后的摘要信息;
+ *
+ * 这是算法标识 {@link #getAlgorithm()} 与原始的摘要数据 {@link #getRawDigest()}
+ * 按照特定的编码方式合并后的结果;
+ */
+ @Override
+ byte[] toBytes();
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoDigest.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoDigest.java
new file mode 100644
index 00000000..da7e9e07
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoDigest.java
@@ -0,0 +1,18 @@
+package com.jd.blockchain.crypto;
+
+/**
+ * 摘要;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface CryptoDigest extends CryptoBytes {
+
+ /**
+ * 原始的摘要数据;
+ *
+ * @return
+ */
+ byte[] getRawDigest();
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoException.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoException.java
new file mode 100644
index 00000000..f4daf036
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoException.java
@@ -0,0 +1,16 @@
+package com.jd.blockchain.crypto;
+
+public class CryptoException extends RuntimeException {
+
+ private static final long serialVersionUID = 1044893802336696205L;
+
+
+ public CryptoException(String message) {
+ super(message);
+ }
+
+ public CryptoException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoFactory.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoFactory.java
new file mode 100644
index 00000000..5cf15bbf
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoFactory.java
@@ -0,0 +1,15 @@
+package com.jd.blockchain.crypto;
+
+import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography;
+import com.jd.blockchain.crypto.hash.HashCryptography;
+import com.jd.blockchain.crypto.symmetric.SymmetricCryptography;
+
+public interface CryptoFactory {
+
+ HashCryptography hashCryptography();
+
+ AsymmetricCryptography asymmetricCryptography();
+
+ SymmetricCryptography symmetricCryptography();
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoFunction.java
new file mode 100644
index 00000000..7c781eea
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoFunction.java
@@ -0,0 +1,7 @@
+package com.jd.blockchain.crypto;
+
+public interface CryptoFunction {
+
+ CryptoAlgorithm getAlgorithm();
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKey.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKey.java
new file mode 100644
index 00000000..ea73ef25
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKey.java
@@ -0,0 +1,25 @@
+package com.jd.blockchain.crypto;
+
+/**
+ * 密钥;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface CryptoKey extends CryptoBytes {
+
+ /**
+ * 密钥的类型;
+ * @return
+ */
+ CryptoKeyType getKeyType();
+
+ /**
+ * 原始的密钥数据;
+ *
+ * @return
+ */
+ byte[] getRawKeyBytes();
+
+
+}
\ No newline at end of file
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKeyPairGenerator.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKeyPairGenerator.java
new file mode 100644
index 00000000..2e3c9da9
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKeyPairGenerator.java
@@ -0,0 +1,14 @@
+package com.jd.blockchain.crypto;
+
+import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair;
+
+public interface CryptoKeyPairGenerator {
+
+ /**
+ * 返回密钥对;
+ */
+ CryptoKeyPair generateKeyPair();
+
+
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKeyType.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKeyType.java
new file mode 100644
index 00000000..7c94d2df
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKeyType.java
@@ -0,0 +1,38 @@
+package com.jd.blockchain.crypto;
+
+public enum CryptoKeyType {
+
+ /**
+ * 非对称密码算法的公钥
+ */
+ PUB_KEY((byte)0x01),
+
+ /**
+ * 非对称密码算法的私钥;
+ */
+ PRIV_KEY((byte)0x02),
+
+ /**
+ * 对称密码算法的密钥;
+ */
+ SYMMETRIC_KEY((byte)0x03);
+
+ public final byte CODE;
+
+ CryptoKeyType(byte code) {
+ CODE = code;
+ }
+
+ public static CryptoKeyType valueOf(byte code) {
+ for (CryptoKeyType alg : CryptoKeyType.values()) {
+ if (alg.CODE == code) {
+ return alg;
+ }
+ }
+ throw new IllegalArgumentException("CryptoKeyType doesn't support enum code[" + code + "]!");
+ }
+
+ public byte getCODE() {
+ return CODE;
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoSymmetricKeyGenerator.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoSymmetricKeyGenerator.java
new file mode 100644
index 00000000..baafb599
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoSymmetricKeyGenerator.java
@@ -0,0 +1,9 @@
+package com.jd.blockchain.crypto;
+
+public interface CryptoSymmetricKeyGenerator {
+
+ /**
+ * 返回对称密钥;
+ */
+ CryptoKey generateSymmetricKey();
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoUtils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoUtils.java
new file mode 100644
index 00000000..4493a681
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoUtils.java
@@ -0,0 +1,51 @@
+package com.jd.blockchain.crypto;
+
+import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography;
+import com.jd.blockchain.crypto.asymmetric.AsymmetricEncryptionFunction;
+import com.jd.blockchain.crypto.asymmetric.SignatureFunction;
+import com.jd.blockchain.crypto.hash.HashCryptography;
+import com.jd.blockchain.crypto.hash.HashFunction;
+import com.jd.blockchain.crypto.impl.CryptoFactoryImpl;
+import com.jd.blockchain.crypto.symmetric.SymmetricCryptography;
+import com.jd.blockchain.crypto.symmetric.SymmetricEncryptionFunction;
+
+public class CryptoUtils {
+
+ private static CryptoFactory CRYPTO_FACTORY = new CryptoFactoryImpl();
+
+ public static CryptoFactory crypto() {
+ return CRYPTO_FACTORY;
+ }
+
+
+ public static HashCryptography hashCrypto() {
+ return crypto().hashCryptography();
+ }
+
+ public static HashFunction hash(CryptoAlgorithm alg) {
+ return hashCrypto().getFunction(alg);
+ }
+
+
+ public static AsymmetricCryptography asymmCrypto() {
+ return crypto().asymmetricCryptography();
+ }
+
+ public static SignatureFunction sign(CryptoAlgorithm alg) {
+ return asymmCrypto().getSignatureFunction(alg);
+ }
+
+ public static AsymmetricEncryptionFunction asymmEncrypt(CryptoAlgorithm alg) {
+ return asymmCrypto().getAsymmetricEncryptionFunction(alg);
+ }
+
+
+ public static SymmetricCryptography symmCrypto() {
+ return crypto().symmetricCryptography();
+ }
+
+ public static SymmetricEncryptionFunction symmEncrypt(CryptoAlgorithm alg) {
+ return symmCrypto().getSymmetricEncryptionFunction(alg);
+ }
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricCiphertext.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricCiphertext.java
new file mode 100644
index 00000000..f28d3d83
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricCiphertext.java
@@ -0,0 +1,26 @@
+package com.jd.blockchain.crypto.asymmetric;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.base.BaseCryptoBytes;
+
+public class AsymmetricCiphertext extends BaseCryptoBytes implements Ciphertext {
+
+ public AsymmetricCiphertext(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) {
+ super(algorithm, rawCryptoBytes);
+ }
+
+ public AsymmetricCiphertext(byte[] cryptoBytes) {
+ super(cryptoBytes);
+ }
+
+ @Override
+ protected boolean support(CryptoAlgorithm algorithm) {
+ return algorithm.isAsymmetric() && algorithm.isEncryptable();
+ }
+
+ @Override
+ public byte[] getRawCiphertext() {
+ return getRawCryptoBytes().getBytesCopy();
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricCryptography.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricCryptography.java
new file mode 100644
index 00000000..475990f9
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricCryptography.java
@@ -0,0 +1,72 @@
+package com.jd.blockchain.crypto.asymmetric;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+
+public interface AsymmetricCryptography {
+
+ /**
+ * 生成密钥对;
+ *
+ * @param algorithm
+ * @return
+ */
+ CryptoKeyPair generateKeyPair(CryptoAlgorithm algorithm);
+
+ /**
+ * 获取签名方法;
+ *
+ * @param algorithm
+ * @return
+ */
+ SignatureFunction getSignatureFunction(CryptoAlgorithm algorithm);
+
+ /**
+ * 校验签名摘要和数据是否一致;
+ *
+ * @param digestBytes 签名摘要数据
+ * @param pubKeyBytes 公钥数据
+ * @param data 被签名数据
+ * @return
+ */
+ boolean verify(byte[] digestBytes, byte[] pubKeyBytes, byte[] data);
+
+ /**
+ * 获取非对称加密方法;
+ *
+ * @param algorithm
+ * @return
+ */
+ AsymmetricEncryptionFunction getAsymmetricEncryptionFunction(CryptoAlgorithm algorithm);
+
+ /**
+ * 解密;
+ *
+ * @param privKeyBytes
+ * @param ciphertextBytes
+ * @return
+ */
+ byte[] decrypt(byte[] privKeyBytes, byte[] ciphertextBytes);
+
+
+ Ciphertext resolveCiphertext(byte[] ciphertextBytes);
+
+ Ciphertext tryResolveCiphertext(byte[] ciphertextBytes);
+
+ /**
+ * @param digestBytes 待解析签名摘要
+ * @return
+ */
+ SignatureDigest resolveSignatureDigest(byte[] digestBytes);
+
+ SignatureDigest tryResolveSignatureDigest(byte[] digestBytes);
+
+ PubKey resolvePubKey(byte[] pubKeyBytes);
+
+ PubKey tryResolvePubKey(byte[] pubKeyBytes);
+
+ PrivKey resolvePrivKey(byte[] privKeyBytes);
+
+ PrivKey tryResolvePrivKey(byte[] privKeyBytes);
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricEncryptionFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricEncryptionFunction.java
new file mode 100644
index 00000000..8357d54e
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricEncryptionFunction.java
@@ -0,0 +1,74 @@
+package com.jd.blockchain.crypto.asymmetric;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoFunction;
+import com.jd.blockchain.crypto.CryptoKeyPairGenerator;
+
+public interface AsymmetricEncryptionFunction extends CryptoKeyPairGenerator, CryptoFunction {
+
+ /**
+ * 加密;
+ *
+ * @param data
+ * @return
+ */
+ Ciphertext encrypt(PubKey pubKey, byte[] data);
+
+ /**
+ * 解密;
+ *
+ * @param privKey
+ * @param ciphertext
+ * @return
+ */
+ byte[] decrypt(PrivKey privKey, Ciphertext ciphertext);
+
+ /**
+ * 校验私钥格式是否满足要求;
+ *
+ * @param privKeyBytes 包含算法标识、密钥掩码和私钥的字节数组
+ * @return 是否满足指定算法的私钥格式
+ */
+ boolean supportPrivKey(byte[] privKeyBytes);
+
+ /**
+ * 将字节数组形式的私钥转换成PrivKey格式;
+ *
+ * @param privKeyBytes 包含算法标识和私钥的字节数组
+ * @return PrivKey形式的私钥
+ */
+ PrivKey resolvePrivKey(byte[] privKeyBytes);
+
+ /**
+ * 校验公钥格式是否满足要求;
+ *
+ * @param pubKeyBytes 包含算法标识、密钥掩码和公钥的字节数组
+ * @return 是否满足指定算法的公钥格式
+ */
+ boolean supportPubKey(byte[] pubKeyBytes);
+
+ /**
+ * 将字节数组形式的密钥转换成PubKey格式;
+ *
+ * @param pubKeyBytes 包含算法标识和公钥的字节数组
+ * @return PubKey形式的公钥
+ */
+ PubKey resolvePubKey(byte[] pubKeyBytes);
+
+ /**
+ * 校验密文格式是否满足要求;
+ *
+ * @param ciphertextBytes 包含算法标识和密文的字节数组
+ * @return 是否满足指定算法的密文格式
+ */
+ boolean supportCiphertext(byte[] ciphertextBytes);
+
+ /**
+ * 将字节数组形式的密文转换成AsymmetricCiphertext格式;
+ *
+ * @param ciphertextBytes 包含算法标识和密文的字节数组
+ * @return AsymmetricCiphertext形式的签名摘要
+ */
+ AsymmetricCiphertext resolveCiphertext(byte[] ciphertextBytes);
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/CryptoKeyPair.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/CryptoKeyPair.java
new file mode 100644
index 00000000..1dd023b2
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/CryptoKeyPair.java
@@ -0,0 +1,22 @@
+package com.jd.blockchain.crypto.asymmetric;
+
+public class CryptoKeyPair {
+
+ private PubKey pubKey;
+
+ private PrivKey privKey;
+
+ public PubKey getPubKey() {
+ return pubKey;
+ }
+
+ public PrivKey getPrivKey() {
+ return privKey;
+ }
+
+ public CryptoKeyPair(PubKey pubKey, PrivKey privKey) {
+ this.pubKey = pubKey;
+ this.privKey = privKey;
+ }
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/PrivKey.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/PrivKey.java
new file mode 100644
index 00000000..b756b9f2
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/PrivKey.java
@@ -0,0 +1,28 @@
+package com.jd.blockchain.crypto.asymmetric;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoKeyType;
+import com.jd.blockchain.crypto.base.BaseCryptoKey;
+
+/**
+ * 私钥;
+ *
+ * @author huanghaiquan
+ *
+ */
+public class PrivKey extends BaseCryptoKey {
+ private static final long serialVersionUID = 6265440395252295646L;
+
+ public PrivKey(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) {
+ super(algorithm, rawCryptoBytes, CryptoKeyType.PRIV_KEY);
+ }
+
+ public PrivKey(byte[] cryptoBytes) {
+ super(cryptoBytes);
+ }
+
+ @Override
+ protected boolean support(CryptoKeyType keyType) {
+ return CryptoKeyType.PRIV_KEY == keyType;
+ }
+}
\ No newline at end of file
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/PubKey.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/PubKey.java
new file mode 100644
index 00000000..328b0751
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/PubKey.java
@@ -0,0 +1,32 @@
+package com.jd.blockchain.crypto.asymmetric;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoKeyType;
+import com.jd.blockchain.crypto.base.BaseCryptoKey;
+
+/**
+ * 公钥;
+ *
+ * @author huanghaiquan
+ *
+ */
+public class PubKey extends BaseCryptoKey {
+
+ private static final long serialVersionUID = -2055071197736385328L;
+
+ public PubKey(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) {
+ super(algorithm, rawCryptoBytes, CryptoKeyType.PUB_KEY);
+ }
+ public PubKey() {
+ super();
+ }
+
+ public PubKey(byte[] cryptoBytes) {
+ super(cryptoBytes);
+ }
+
+ @Override
+ protected boolean support(CryptoKeyType keyType) {
+ return CryptoKeyType.PUB_KEY == keyType;
+ }
+}
\ No newline at end of file
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/SignatureDigest.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/SignatureDigest.java
new file mode 100644
index 00000000..9e6e17de
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/SignatureDigest.java
@@ -0,0 +1,35 @@
+package com.jd.blockchain.crypto.asymmetric;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoDigest;
+import com.jd.blockchain.crypto.base.BaseCryptoBytes;
+
+public class SignatureDigest extends BaseCryptoBytes implements CryptoDigest {
+ public SignatureDigest() {
+ super();
+ }
+
+ public SignatureDigest(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) {
+ super(algorithm, rawCryptoBytes);
+ }
+
+ public SignatureDigest(byte[] cryptoBytes) {
+ super(cryptoBytes);
+ }
+
+ @Override
+ protected boolean support(CryptoAlgorithm algorithm) {
+ return algorithm.isAsymmetric() && algorithm.isSignable();
+ }
+
+ /**
+ * 返回原始签名摘要;
+ *
+ * @return
+ */
+ @Override
+ public byte[] getRawDigest() {
+ return getRawCryptoBytes().getBytesCopy();
+ }
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/SignatureFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/SignatureFunction.java
new file mode 100644
index 00000000..cdfdc9bd
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/SignatureFunction.java
@@ -0,0 +1,73 @@
+package com.jd.blockchain.crypto.asymmetric;
+
+import com.jd.blockchain.crypto.CryptoFunction;
+import com.jd.blockchain.crypto.CryptoKeyPairGenerator;
+
+public interface SignatureFunction extends CryptoKeyPairGenerator, CryptoFunction {
+
+ /**
+ * 计算指定数据的 hash;
+ *
+ * @param data 被签名消息
+ * @return SignatureDigest形式的签名摘要
+ */
+ SignatureDigest sign(PrivKey privKey, byte[] data);
+
+ /**
+ * 校验签名摘要和数据是否一致;
+ *
+ * @param digest 待验证的签名摘要
+ * @param data 被签名信息
+ * @return 是否验证通过
+ */
+ boolean verify(SignatureDigest digest, PubKey pubKey, byte[] data);
+
+ /**
+ * 校验私钥格式是否满足要求;
+ *
+ * @param privKeyBytes 包含算法标识、密钥掩码和私钥的字节数组
+ * @return 是否满足指定算法的私钥格式
+ */
+ boolean supportPrivKey(byte[] privKeyBytes);
+
+ /**
+ * 将字节数组形式的私钥转换成PrivKey格式;
+ *
+ * @param privKeyBytes 包含算法标识、密钥掩码和私钥的字节数组
+ * @return PrivKey形式的私钥
+ */
+ PrivKey resolvePrivKey(byte[] privKeyBytes);
+
+ /**
+ * 校验公钥格式是否满足要求;
+ *
+ * @param pubKeyBytes 包含算法标识、密钥掩码和公钥的字节数组
+ * @return 是否满足指定算法的公钥格式
+ */
+ boolean supportPubKey(byte[] pubKeyBytes);
+
+ /**
+ * 将字节数组形式的密钥转换成PubKey格式;
+ *
+ * @param pubKeyBytes 包含算法标识、密钥掩码和公钥的字节数组
+ * @return PubKey形式的公钥
+ */
+ PubKey resolvePubKey(byte[] pubKeyBytes);
+
+ /**
+ * 校验字节数组形式的签名摘要的格式是否满足要求;
+ *
+ * @param digestBytes 包含算法标识和签名摘要的字节数组
+ * @return 是否满足指定算法的签名摘要格式
+ */
+
+ boolean supportDigest(byte[] digestBytes);
+
+ /**
+ * 将字节数组形式的签名摘要转换成SignatureDigest格式;
+ *
+ * @param digestBytes 包含算法标识和签名摘要的字节数组
+ * @return SignatureDigest形式的签名摘要
+ */
+ SignatureDigest resolveDigest(byte[] digestBytes);
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/base/BaseCryptoBytes.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/base/BaseCryptoBytes.java
new file mode 100644
index 00000000..3fc2ac70
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/base/BaseCryptoBytes.java
@@ -0,0 +1,56 @@
+package com.jd.blockchain.crypto.base;
+
+import java.util.Arrays;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoBytes;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public abstract class BaseCryptoBytes extends Bytes implements CryptoBytes {
+
+ private CryptoAlgorithm algorithm;
+
+ public BaseCryptoBytes() {
+ super();
+ }
+
+ public BaseCryptoBytes(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) {
+ super(encodeBytes(algorithm, rawCryptoBytes));
+ this.algorithm = algorithm;
+ }
+
+ public BaseCryptoBytes(byte[] cryptoBytes) {
+ super(cryptoBytes);
+ CryptoAlgorithm algorithm = decodeAlgorithm(cryptoBytes);
+ if (!support(algorithm)) {
+ throw new IllegalArgumentException("Not supported algorithm[" + algorithm.toString() + "]!");
+ }
+ this.algorithm = algorithm;
+ }
+
+ static byte[] encodeBytes(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) {
+ return BytesUtils.concat(new byte[] { algorithm.CODE }, rawCryptoBytes);
+ }
+
+ static CryptoAlgorithm decodeAlgorithm(byte[] cryptoBytes) {
+ return CryptoAlgorithm.valueOf(cryptoBytes[0]);
+ }
+
+ protected abstract boolean support(CryptoAlgorithm algorithm);
+
+ protected byte[] resolveRawCryptoBytes(byte[] cryptoBytes) {
+ return Arrays.copyOfRange(cryptoBytes, 1, cryptoBytes.length);
+ }
+
+ public CryptoAlgorithm getAlgorithm() {
+ // return resolveAlgorithm(encodedBytes);
+ return algorithm;
+ }
+
+ protected BytesSlice getRawCryptoBytes() {
+ // return resolveRawCryptoBytes(encodedBytes);
+ return new BytesSlice(getDirectBytes(), 1);
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/base/BaseCryptoKey.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/base/BaseCryptoKey.java
new file mode 100644
index 00000000..de6eeabf
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/base/BaseCryptoKey.java
@@ -0,0 +1,62 @@
+package com.jd.blockchain.crypto.base;
+
+import java.io.Serializable;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoException;
+import com.jd.blockchain.crypto.CryptoKey;
+import com.jd.blockchain.crypto.CryptoKeyType;
+import com.jd.blockchain.utils.io.BytesSlice;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public abstract class BaseCryptoKey extends BaseCryptoBytes implements CryptoKey,Serializable {
+
+ public static final int KEY_TYPE_BYTES = 1;
+ private static final long serialVersionUID = 4543074827807908363L;
+
+ private CryptoKeyType keyType;
+
+ public BaseCryptoKey() {
+ super();
+ }
+
+ public BaseCryptoKey(CryptoAlgorithm algorithm, byte[] rawCryptoBytes, CryptoKeyType keyType) {
+ super(algorithm, encodeKeyBytes(rawCryptoBytes, keyType));
+ this.keyType = keyType;
+ }
+
+ public BaseCryptoKey(byte[] cryptoBytes) {
+ super(cryptoBytes);
+ CryptoKeyType keyType = decodeKeyType(getRawCryptoBytes());
+ if (!support(keyType)) {
+ throw new CryptoException("CryptoKey doesn't support keyType[" + keyType + "]!");
+ }
+ this.keyType = keyType;
+ }
+
+ @Override
+ public CryptoKeyType getKeyType() {
+ return keyType;
+ }
+
+ private static byte[] encodeKeyBytes(byte[] rawCryptoBytes, CryptoKeyType keyType ) {
+ return BytesUtils.concat(new byte[] {keyType.CODE }, rawCryptoBytes);
+ }
+
+ private static CryptoKeyType decodeKeyType(BytesSlice cryptoBytes) {
+ return CryptoKeyType.valueOf(cryptoBytes.getByte());
+ }
+
+ protected abstract boolean support(CryptoKeyType keyType);
+
+ @Override
+ protected boolean support(CryptoAlgorithm algorithm) {
+ return algorithm.isSymmetric() || algorithm.isAsymmetric();
+ }
+
+
+ @Override
+ public byte[] getRawKeyBytes() {
+ return getRawCryptoBytes().getBytesCopy(1);
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashCryptography.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashCryptography.java
new file mode 100644
index 00000000..52c8240d
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashCryptography.java
@@ -0,0 +1,50 @@
+package com.jd.blockchain.crypto.hash;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoException;
+
+public interface HashCryptography {
+
+ /**
+ * return HashFunction instance of the specified hash alg;
+ *
+ *
+ * if alg out of hash alg,then throws {@link IllegalArgumentException}
+ *
+ * @param algorithm
+ * @return
+ */
+ HashFunction getFunction(CryptoAlgorithm algorithm);
+
+ /**
+ * 校验 hash 摘要与指定的数据是否匹配;
+ *
+ * @param digestBytes
+ * @param data
+ * @return
+ */
+ boolean verify(byte[] digestBytes, byte[] data);
+
+ boolean verify(HashDigest digest, byte[] data);
+
+ /**
+ * 解析指定的 hash 摘要;
+ *
+ * 如果不符合哈希摘要的编码格式,则引发 {@link CryptoException} 异常;
+ *
+ * @param digestBytes
+ * @return
+ */
+ HashDigest resolveHashDigest(byte[] digestBytes);
+
+ /**
+ * 解析指定的 hash 摘要;
+ *
+ * 如果不符合哈希摘要的编码格式,则返回 null;
+ *
+ * @param digestBytes
+ * @return
+ */
+ HashDigest tryResolveHashDigest(byte[] digestBytes);
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashDigest.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashDigest.java
new file mode 100644
index 00000000..e19ff3fb
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashDigest.java
@@ -0,0 +1,35 @@
+package com.jd.blockchain.crypto.hash;
+
+import java.io.Serializable;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoDigest;
+import com.jd.blockchain.crypto.base.BaseCryptoBytes;
+
+public class HashDigest extends BaseCryptoBytes implements CryptoDigest,Serializable {
+
+ private static final long serialVersionUID = 693895170514236428L;
+
+ public HashDigest(CryptoAlgorithm algorithm, byte[] rawDigestBytes) {
+ super(algorithm, rawDigestBytes);
+ }
+
+ public HashDigest() {
+ super();
+ }
+
+ public HashDigest(byte[] encodedDigestBytes) {
+ super(encodedDigestBytes);
+ }
+
+ @Override
+ protected boolean support(CryptoAlgorithm algorithm) {
+ return algorithm.isHash();
+ }
+
+ @Override
+ public byte[] getRawDigest() {
+ return getRawCryptoBytes().getBytesCopy();
+ }
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashFunction.java
new file mode 100644
index 00000000..c5bec189
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashFunction.java
@@ -0,0 +1,41 @@
+package com.jd.blockchain.crypto.hash;
+
+import com.jd.blockchain.crypto.CryptoFunction;
+
+public interface HashFunction extends CryptoFunction {
+
+ /**
+ * 计算指定数据的 hash;
+ *
+ * @param data
+ * @return
+ */
+ HashDigest hash(byte[] data);
+
+
+ /**
+ * 校验 hash 摘要与指定的数据是否匹配;
+ *
+ * @param digest
+ * @param data
+ * @return
+ */
+ boolean verify(HashDigest digest, byte[] data);
+
+ /**
+ * 校验字节数组形式的hash摘要的格式是否满足要求;
+ *
+ * @param digestBytes 包含算法标识和hash摘要的字节数组
+ * @return 是否满足指定算法的hash摘要格式
+ */
+ boolean supportHashDigest(byte[] digestBytes);
+
+ /**
+ * 将字节数组形式的hash摘要转换成HashDigest格式;
+ *
+ * @param digestBytes 包含算法标识和hash摘要的字节数组
+ * @return HashDigest形式的hash摘要
+ */
+ HashDigest resolveHashDigest(byte[] digestBytes);
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/AsymmtricCryptographyImpl.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/AsymmtricCryptographyImpl.java
new file mode 100644
index 00000000..2d18626c
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/AsymmtricCryptographyImpl.java
@@ -0,0 +1,194 @@
+package com.jd.blockchain.crypto.impl;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.asymmetric.*;
+import com.jd.blockchain.crypto.impl.def.asymmetric.ED25519SignatureFunction;
+import com.jd.blockchain.crypto.impl.jni.asymmetric.JNIED25519SignatureFunction;
+import com.jd.blockchain.crypto.impl.sm.asymmetric.SM2CryptoFunction;
+
+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.isAsymmetric()){
+ return getSignatureFunction(algorithm).generateKeyPair();
+ }
+ else if (algorithm.isEncryptable() && algorithm.isAsymmetric()){
+ 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 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;
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/CryptoFactoryImpl.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/CryptoFactoryImpl.java
new file mode 100644
index 00000000..90a6d719
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/CryptoFactoryImpl.java
@@ -0,0 +1,30 @@
+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;
+ }
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/HashCryptographyImpl.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/HashCryptographyImpl.java
new file mode 100644
index 00000000..ae1772c5
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/HashCryptographyImpl.java
@@ -0,0 +1,84 @@
+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.def.hash.RIPEMD160HashFunction;
+import com.jd.blockchain.crypto.impl.def.hash.SHA256HashFunction;
+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;
+
+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;
+ }
+}
\ No newline at end of file
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/SymmetricCryptographyImpl.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/SymmetricCryptographyImpl.java
new file mode 100644
index 00000000..ce787511
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/SymmetricCryptographyImpl.java
@@ -0,0 +1,101 @@
+package com.jd.blockchain.crypto.impl;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.impl.def.symmetric.AESSymmetricEncryptionFunction;
+import com.jd.blockchain.crypto.impl.sm.symmetric.SM4SymmetricEncryptionFunction;
+import com.jd.blockchain.crypto.symmetric.SymmetricCryptography;
+import com.jd.blockchain.crypto.symmetric.SymmetricEncryptionFunction;
+import com.jd.blockchain.crypto.symmetric.SymmetricKey;
+
+public class SymmetricCryptographyImpl implements SymmetricCryptography {
+
+ private static final SymmetricEncryptionFunction AES_ENCF = new AESSymmetricEncryptionFunction();
+ private static final SymmetricEncryptionFunction SM4_ENCF = new SM4SymmetricEncryptionFunction();
+
+ /**
+ * 封装了对称密码算法对应的密钥生成算法
+ */
+ @Override
+ public SymmetricKey generateKey(CryptoAlgorithm algorithm) {
+
+ //验证算法标识是对称加密算法,并根据算法生成对称密钥,否则抛出异常
+ if (algorithm.isEncryptable() && algorithm.isSymmetric() ){
+ return (SymmetricKey) 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对应的算法
+ SymmetricKey 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 SymmetricKey resolveSymmetricKey(byte[] symmetricKeyBytes) {
+ SymmetricKey symmetricKey = tryResolveSymmetricKey(symmetricKeyBytes);
+ if (symmetricKey == null)
+ throw new IllegalArgumentException("This symmetricKeyBytes cannot be resolved!");
+ else return symmetricKey;
+ }
+
+ @Override
+ public SymmetricKey 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;
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/asymmetric/ECDSASignatureFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/asymmetric/ECDSASignatureFunction.java
new file mode 100644
index 00000000..7cacb1fe
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/asymmetric/ECDSASignatureFunction.java
@@ -0,0 +1,57 @@
+package com.jd.blockchain.crypto.impl.def.asymmetric;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.asymmetric.*;
+
+public class ECDSASignatureFunction implements SignatureFunction {
+
+ @Override
+ public SignatureDigest sign(PrivKey privKey, byte[] data) {
+ return null;
+ }
+
+ @Override
+ public boolean verify(SignatureDigest digest, PubKey pubKey, byte[] data) {
+ return false;
+ }
+
+ @Override
+ public boolean supportPrivKey(byte[] privKeyBytes) {
+ return false;
+ }
+
+ @Override
+ public PrivKey resolvePrivKey(byte[] privKeyBytes) {
+ return null;
+ }
+
+ @Override
+ public boolean supportPubKey(byte[] pubKeyBytes) {
+ return false;
+ }
+
+ @Override
+ public PubKey resolvePubKey(byte[] pubKeyBytes) {
+ return null;
+ }
+
+ @Override
+ public boolean supportDigest(byte[] digestBytes) {
+ return false;
+ }
+
+ @Override
+ public SignatureDigest resolveDigest(byte[] digestBytes) {
+ return null;
+ }
+
+ @Override
+ public CryptoAlgorithm getAlgorithm() {
+ return CryptoAlgorithm.ECDSA;
+ }
+
+ @Override
+ public CryptoKeyPair generateKeyPair() {
+ return null;
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/asymmetric/ED25519SignatureFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/asymmetric/ED25519SignatureFunction.java
new file mode 100644
index 00000000..0a28b3fc
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/asymmetric/ED25519SignatureFunction.java
@@ -0,0 +1,123 @@
+package com.jd.blockchain.crypto.impl.def.asymmetric;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.asymmetric.*;
+import com.jd.blockchain.utils.security.Ed25519Utils;
+
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.EdDSAPublicKey;
+import net.i2p.crypto.eddsa.KeyPairGenerator;
+
+import java.security.KeyPair;
+
+import static com.jd.blockchain.crypto.CryptoAlgorithm.ED25519;
+import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES;
+import static com.jd.blockchain.crypto.CryptoKeyType.PRIV_KEY;
+import static com.jd.blockchain.crypto.CryptoKeyType.PUB_KEY;
+import static com.jd.blockchain.crypto.base.BaseCryptoKey.KEY_TYPE_BYTES;
+
+public class ED25519SignatureFunction implements SignatureFunction {
+
+ private static final int PUBKEY_SIZE = 32;
+ private static final int PRIVKEY_SIZE = 32;
+ private static final int DIGEST_SIZE = 64;
+
+ private static final int PUBKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + PUBKEY_SIZE;
+ private static final int PRIVKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + PRIVKEY_SIZE;
+ private static final int SIGNATUREDIGEST_LENGTH = ALGORYTHM_BYTES + DIGEST_SIZE;
+
+ public ED25519SignatureFunction() {
+ }
+
+ @Override
+ public SignatureDigest sign(PrivKey privKey, byte[] data) {
+
+ byte[] rawPrivKeyBytes = privKey.getRawKeyBytes();
+
+ // 验证原始私钥长度为256比特,即32字节
+ if (rawPrivKeyBytes.length != PRIVKEY_SIZE)
+ throw new IllegalArgumentException("This key has wrong format!");
+
+ // 验证密钥数据的算法标识对应ED25519签名算法
+ if (privKey.getAlgorithm() != ED25519)
+ throw new IllegalArgumentException("This key is not ED25519 private key!");
+
+ // 调用ED25519签名算法计算签名结果
+ return new SignatureDigest(ED25519, Ed25519Utils.sign_512(data, rawPrivKeyBytes));
+ }
+
+ @Override
+ public boolean verify(SignatureDigest digest, PubKey pubKey, byte[] data) {
+
+ byte[] rawPubKeyBytes = pubKey.getRawKeyBytes();
+ byte[] rawDigestBytes = digest.getRawDigest();
+
+ // 验证原始公钥长度为256比特,即32字节
+ if (rawPubKeyBytes.length != PUBKEY_SIZE)
+ throw new IllegalArgumentException("This key has wrong format!");
+
+ // 验证密钥数据的算法标识对应ED25519签名算法
+ if (pubKey.getAlgorithm() != ED25519)
+ throw new IllegalArgumentException("This key is not ED25519 public key!");
+
+ // 验证密文数据的算法标识对应ED25519签名算法,并且原始摘要长度为64字节
+ if (digest.getAlgorithm() != ED25519 || rawDigestBytes.length != DIGEST_SIZE)
+ throw new IllegalArgumentException("This is not ED25519 signature digest!");
+
+ // 调用ED25519验签算法验证签名结果
+ return Ed25519Utils.verify(data, rawPubKeyBytes, rawDigestBytes);
+ }
+
+ @Override
+ public boolean supportPrivKey(byte[] privKeyBytes) {
+ // 验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,密钥数据的算法标识对应ED25519签名算法,并且密钥类型是私钥
+ return privKeyBytes.length == PRIVKEY_LENGTH && privKeyBytes[0] == ED25519.CODE && privKeyBytes[1] == PRIV_KEY.CODE;
+ }
+
+ @Override
+ public PrivKey resolvePrivKey(byte[] privKeyBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new PrivKey(privKeyBytes);
+ }
+
+ @Override
+ public boolean supportPubKey(byte[] pubKeyBytes) {
+ // 验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,密钥数据的算法标识对应ED25519签名算法,并且密钥类型是公钥
+ return pubKeyBytes.length == PUBKEY_LENGTH && pubKeyBytes[0] == ED25519.CODE && pubKeyBytes[1] == PUB_KEY.CODE;
+
+ }
+
+ @Override
+ public PubKey resolvePubKey(byte[] pubKeyBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new PubKey(pubKeyBytes);
+ }
+
+ @Override
+ public boolean supportDigest(byte[] digestBytes) {
+ // 验证输入字节数组长度=算法标识长度+摘要长度,字节数组的算法标识对应ED25519算法
+ return digestBytes.length == SIGNATUREDIGEST_LENGTH && digestBytes[0] == ED25519.CODE;
+ }
+
+ @Override
+ public SignatureDigest resolveDigest(byte[] digestBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new SignatureDigest(digestBytes);
+ }
+
+ @Override
+ public CryptoAlgorithm getAlgorithm() {
+ return ED25519;
+ }
+
+ @Override
+ public CryptoKeyPair generateKeyPair() {
+ // 调用ED25519算法的密钥生成算法生成公私钥对priKey和pubKey,返回密钥对
+ KeyPairGenerator keyPairGenerator = new KeyPairGenerator();
+ KeyPair keyPair = keyPairGenerator.generateKeyPair();
+ EdDSAPrivateKey privKey = (EdDSAPrivateKey) keyPair.getPrivate();
+ EdDSAPublicKey pubKey = (EdDSAPublicKey) keyPair.getPublic();
+ return new CryptoKeyPair(new PubKey(ED25519, pubKey.getAbyte()), new PrivKey(ED25519, privKey.getSeed()));
+
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/hash/RIPEMD160HashFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/hash/RIPEMD160HashFunction.java
new file mode 100644
index 00000000..7933021b
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/hash/RIPEMD160HashFunction.java
@@ -0,0 +1,47 @@
+package com.jd.blockchain.crypto.impl.def.hash;
+
+import static com.jd.blockchain.crypto.CryptoAlgorithm.RIPEMD160;
+import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES;
+
+import java.util.Arrays;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.crypto.hash.HashFunction;
+import com.jd.blockchain.utils.security.RipeMD160Utils;
+
+public class RIPEMD160HashFunction implements HashFunction {
+
+ private static final int DIGEST_BYTES = 160 / 8;
+
+ private static final int DIGEST_LENGTH = ALGORYTHM_BYTES + DIGEST_BYTES;
+
+ @Override
+ public CryptoAlgorithm getAlgorithm() {
+ return RIPEMD160;
+ }
+
+ @Override
+ public HashDigest hash(byte[] data) {
+ byte[] digestBytes = RipeMD160Utils.hash(data);
+ return new HashDigest(RIPEMD160, digestBytes);
+ }
+
+ @Override
+ public boolean verify(HashDigest digest, byte[] data) {
+ HashDigest hashDigest = hash(data);
+ return Arrays.equals(hashDigest.toBytes(), digest.toBytes());
+ }
+
+ @Override
+ public boolean supportHashDigest(byte[] digestBytes) {
+ // 验证输入字节数组长度=算法标识长度+摘要长度,以及算法标识;
+ return RIPEMD160.CODE == digestBytes[0] && DIGEST_LENGTH == digestBytes.length;
+ }
+
+ @Override
+ public HashDigest resolveHashDigest(byte[] digestBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new HashDigest(digestBytes);
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/hash/SHA256HashFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/hash/SHA256HashFunction.java
new file mode 100644
index 00000000..b7aebeff
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/hash/SHA256HashFunction.java
@@ -0,0 +1,49 @@
+package com.jd.blockchain.crypto.impl.def.hash;
+
+import static com.jd.blockchain.crypto.CryptoAlgorithm.SHA256;
+import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES;
+
+import java.util.Arrays;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.crypto.hash.HashFunction;
+import com.jd.blockchain.utils.security.ShaUtils;
+
+public class SHA256HashFunction implements HashFunction {
+
+ private static final int DIGEST_BYTES = 256 / 8;
+
+ private static final int DIGEST_LENGTH = ALGORYTHM_BYTES + DIGEST_BYTES;
+
+ @Override
+ public CryptoAlgorithm getAlgorithm() {
+ return SHA256;
+ }
+
+ @Override
+ public HashDigest hash(byte[] data) {
+ byte[] digestBytes = ShaUtils.hash_256(data);
+ return new HashDigest(SHA256, digestBytes);
+ }
+
+ @Override
+ public boolean verify(HashDigest digest, byte[] data) {
+ HashDigest hashDigest = hash(data);
+ return Arrays.equals(hashDigest.toBytes(), digest.toBytes());
+ }
+
+ @Override
+ public boolean supportHashDigest(byte[] digestBytes) {
+ // 验证输入字节数组长度=算法标识长度+摘要长度,以及算法标识;
+ return SHA256.CODE == digestBytes[0] && DIGEST_LENGTH == digestBytes.length;
+ }
+
+ @Override
+ public HashDigest resolveHashDigest(byte[] hashDigestBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new HashDigest(hashDigestBytes);
+ }
+
+}
+
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/symmetric/AESSymmetricEncryptionFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/symmetric/AESSymmetricEncryptionFunction.java
new file mode 100644
index 00000000..64b7cf82
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/symmetric/AESSymmetricEncryptionFunction.java
@@ -0,0 +1,142 @@
+package com.jd.blockchain.crypto.impl.def.symmetric;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoKey;
+import com.jd.blockchain.crypto.symmetric.SymmetricCiphertext;
+import com.jd.blockchain.crypto.symmetric.SymmetricEncryptionFunction;
+import com.jd.blockchain.crypto.symmetric.SymmetricKey;
+import com.jd.blockchain.utils.security.AESUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import static com.jd.blockchain.crypto.CryptoAlgorithm.AES;
+import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES;
+import static com.jd.blockchain.crypto.CryptoKeyType.SYMMETRIC_KEY;
+import static com.jd.blockchain.crypto.base.BaseCryptoKey.KEY_TYPE_BYTES;
+
+public class AESSymmetricEncryptionFunction implements SymmetricEncryptionFunction {
+
+ private static final int KEY_SIZE = 16;
+ private static final int BLOCK_SIZE = 16;
+
+ private static final int SYMMETRICKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + KEY_SIZE;
+
+ public AESSymmetricEncryptionFunction() {
+ }
+
+ @Override
+ public Ciphertext encrypt(SymmetricKey key, byte[] data) {
+
+ byte[] rawKeyBytes = key.getRawKeyBytes();
+
+ // 验证原始密钥长度为128比特,即16字节
+ if (rawKeyBytes.length != KEY_SIZE)
+ throw new IllegalArgumentException("This key has wrong format!");
+
+ // 验证密钥数据的算法标识对应AES算法
+ if (key.getAlgorithm() != AES)
+ throw new IllegalArgumentException("The is not AES symmetric key!");
+
+ // 调用底层AES128算法并计算密文数据
+ return new SymmetricCiphertext(AES, AESUtils.encrypt(data, rawKeyBytes));
+ }
+
+ @Override
+ public void encrypt(SymmetricKey key, InputStream in, OutputStream out) {
+
+ // 读输入流得到明文,加密,密文数据写入输出流
+ try {
+ byte[] aesData = new byte[in.available()];
+ in.read(aesData);
+ in.close();
+
+ out.write(encrypt(key, aesData).toBytes());
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public byte[] decrypt(SymmetricKey key, Ciphertext ciphertext) {
+
+ byte[] rawKeyBytes = key.getRawKeyBytes();
+ byte[] rawCiphertextBytes = ciphertext.getRawCiphertext();
+
+ // 验证原始密钥长度为128比特,即16字节
+ if (rawKeyBytes.length != KEY_SIZE)
+ throw new IllegalArgumentException("This key has wrong format!");
+
+ // 验证密钥数据的算法标识对应AES算法
+ if (key.getAlgorithm().CODE != AES.CODE)
+ throw new IllegalArgumentException("The is not AES symmetric key!");
+
+ // 验证原始密文长度为分组长度的整数倍
+ if (rawCiphertextBytes.length % BLOCK_SIZE != 0)
+ throw new IllegalArgumentException("This ciphertext has wrong format!");
+
+ // 验证密文数据算法标识对应AES算法
+ if (ciphertext.getAlgorithm() != AES)
+ throw new IllegalArgumentException("This is not AES ciphertext!");
+
+ // 调用底层AES128算法解密,得到明文
+ return AESUtils.decrypt(rawCiphertextBytes, rawKeyBytes);
+ }
+
+ @Override
+ public void decrypt(SymmetricKey key, InputStream in, OutputStream out) {
+
+ // 读输入流得到密文数据,解密,明文写入输出流
+ try {
+ byte[] aesData = new byte[in.available()];
+ in.read(aesData);
+ in.close();
+
+ if (!supportCiphertext(aesData))
+ throw new IllegalArgumentException("InputStream is not valid AES ciphertext!");
+
+ out.write(decrypt(key, resolveCiphertext(aesData)));
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public boolean supportSymmetricKey(byte[] symmetricKeyBytes) {
+ //验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,字节数组的算法标识对应AES算法且密钥密钥类型是对称密钥
+ return symmetricKeyBytes.length == SYMMETRICKEY_LENGTH && symmetricKeyBytes[0] == AES.CODE && symmetricKeyBytes[1] == SYMMETRIC_KEY.CODE;
+ }
+
+ @Override
+ public SymmetricKey resolveSymmetricKey(byte[] symmetricKeyBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new SymmetricKey(symmetricKeyBytes);
+ }
+
+ @Override
+ public boolean supportCiphertext(byte[] ciphertextBytes) {
+ // 验证(输入字节数组长度-算法标识长度)是分组长度的整数倍,字节数组的算法标识对应AES算法
+ return (ciphertextBytes.length - ALGORYTHM_BYTES) % BLOCK_SIZE == 0 && ciphertextBytes[0] == AES.CODE;
+ }
+
+ @Override
+ public SymmetricCiphertext resolveCiphertext(byte[] ciphertextBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new SymmetricCiphertext(ciphertextBytes);
+ }
+
+ @Override
+ public CryptoAlgorithm getAlgorithm() {
+ return AES;
+ }
+
+ @Override
+ public CryptoKey generateSymmetricKey() {
+ // 根据对应的标识和原始密钥生成相应的密钥数据
+ return new SymmetricKey(AES, AESUtils.generateKey128_Bytes());
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/asymmetric/JNIED25519SignatureFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/asymmetric/JNIED25519SignatureFunction.java
new file mode 100644
index 00000000..7aa6b80f
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/asymmetric/JNIED25519SignatureFunction.java
@@ -0,0 +1,130 @@
+package com.jd.blockchain.crypto.impl.jni.asymmetric;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.asymmetric.*;
+import com.jd.blockchain.crypto.jniutils.asymmetric.JNIED25519Utils;
+
+import static com.jd.blockchain.crypto.CryptoAlgorithm.JNIED25519;
+import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES;
+import static com.jd.blockchain.crypto.CryptoKeyType.PRIV_KEY;
+import static com.jd.blockchain.crypto.CryptoKeyType.PUB_KEY;
+import static com.jd.blockchain.crypto.base.BaseCryptoKey.KEY_TYPE_BYTES;
+
+public class JNIED25519SignatureFunction implements SignatureFunction {
+
+ private static final int PUBKEY_SIZE = 32;
+ private static final int PRIVKEY_SIZE = 32;
+ private static final int DIGEST_SIZE = 64;
+
+ private static final int PUBKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + PUBKEY_SIZE;
+ private static final int PRIVKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + PRIVKEY_SIZE;
+ private static final int SIGNATUREDIGEST_LENGTH = ALGORYTHM_BYTES + DIGEST_SIZE;
+
+ public JNIED25519SignatureFunction() {
+ }
+
+ @Override
+ public SignatureDigest sign(PrivKey privKey, byte[] data) {
+
+ if (data == null)
+ throw new IllegalArgumentException("This data is null!");
+
+ JNIED25519Utils ed25519 = new JNIED25519Utils();
+
+ byte[] rawPrivKeyBytes = privKey.getRawKeyBytes();
+ byte[] rawPubKeyBytes = ed25519.getPubKey(rawPrivKeyBytes);
+
+ // 验证原始私钥长度为256比特,即32字节
+ if (rawPrivKeyBytes.length != PRIVKEY_SIZE)
+ throw new IllegalArgumentException("This key has wrong format!");
+
+ // 验证密钥数据的算法标识对应JNIED25519签名算法
+ if (privKey.getAlgorithm() != JNIED25519)
+ throw new IllegalArgumentException("This key is not ED25519 private key!");
+
+ // 调用JNIED25519签名算法计算签名结果
+ return new SignatureDigest(JNIED25519, ed25519.sign(data, rawPrivKeyBytes, rawPubKeyBytes));
+ }
+
+ @Override
+ public boolean verify(SignatureDigest digest, PubKey pubKey, byte[] data) {
+
+ JNIED25519Utils ed25519 = new JNIED25519Utils();
+
+ byte[] rawPubKeyBytes = pubKey.getRawKeyBytes();
+ byte[] rawDigestBytes = digest.getRawDigest();
+
+ // 验证原始公钥长度为256比特,即32字节
+ if (rawPubKeyBytes.length != PUBKEY_SIZE)
+ throw new IllegalArgumentException("This key has wrong format!");
+
+ // 验证密钥数据的算法标识对应JNIED25519签名算法
+ if (pubKey.getAlgorithm() != JNIED25519)
+ throw new IllegalArgumentException("This key is not ED25519 public key!");
+
+ // 验证密文数据的算法标识对应JNIED25519签名算法,并且原始摘要长度为64字节
+ if (digest.getAlgorithm() != JNIED25519 || rawDigestBytes.length != DIGEST_SIZE)
+ throw new IllegalArgumentException("This is not ED25519 signature digest!");
+
+ // 调用JNIED25519验签算法验证签名结果
+ return ed25519.verify(data, rawPubKeyBytes, rawDigestBytes);
+ }
+
+ @Override
+ public boolean supportPrivKey(byte[] privKeyBytes) {
+ // 验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,密钥数据的算法标识对应JNIED25519签名算法,并且密钥类型是私钥
+ return privKeyBytes.length == PRIVKEY_LENGTH
+ && privKeyBytes[0] == JNIED25519.CODE && privKeyBytes[1] == PRIV_KEY.CODE;
+ }
+
+ @Override
+ public PrivKey resolvePrivKey(byte[] privKeyBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new PrivKey(privKeyBytes);
+ }
+
+ @Override
+ public boolean supportPubKey(byte[] pubKeyBytes) {
+ // 验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,密钥数据的算法标识对应JNIED25519签名算法,并且密钥类型是公钥
+ return pubKeyBytes.length == PUBKEY_LENGTH &&
+ pubKeyBytes[0] == JNIED25519.CODE && pubKeyBytes[1] == PUB_KEY.CODE;
+
+ }
+
+ @Override
+ public PubKey resolvePubKey(byte[] pubKeyBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new PubKey(pubKeyBytes);
+ }
+
+ @Override
+ public boolean supportDigest(byte[] digestBytes) {
+ // 验证输入字节数组长度=算法标识长度+摘要长度,字节数组的算法标识对应JNIED25519算法
+ return digestBytes.length == SIGNATUREDIGEST_LENGTH && digestBytes[0] == JNIED25519.CODE;
+ }
+
+ @Override
+ public SignatureDigest resolveDigest(byte[] digestBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new SignatureDigest(digestBytes);
+ }
+
+ @Override
+ public CryptoAlgorithm getAlgorithm() {
+ return JNIED25519;
+ }
+
+ @Override
+ public CryptoKeyPair generateKeyPair() {
+
+ JNIED25519Utils ed25519 = new JNIED25519Utils();
+ byte[] rawPrivKeyBytes = new byte[PRIVKEY_SIZE];
+ byte[] rawPubKeyBytes = new byte[PUBKEY_SIZE];
+
+ // 调用JNIED25519算法的密钥生成算法生成公私钥对
+ ed25519.generateKeyPair(rawPrivKeyBytes, rawPubKeyBytes);
+
+ return new CryptoKeyPair(new PubKey(JNIED25519, rawPubKeyBytes), new PrivKey(JNIED25519, rawPrivKeyBytes));
+
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/hash/JNIRIPEMD160HashFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/hash/JNIRIPEMD160HashFunction.java
new file mode 100644
index 00000000..72d0777e
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/hash/JNIRIPEMD160HashFunction.java
@@ -0,0 +1,53 @@
+package com.jd.blockchain.crypto.impl.jni.hash;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.crypto.hash.HashFunction;
+import com.jd.blockchain.crypto.jniutils.hash.JNIRIPEMD160Utils;
+
+import java.util.Arrays;
+
+import static com.jd.blockchain.crypto.CryptoAlgorithm.JNIRIPEMD160;
+import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES;
+
+public class JNIRIPEMD160HashFunction implements HashFunction {
+
+ private static final int DIGEST_BYTES = 160 / 8;
+
+ private static final int DIGEST_LENGTH = ALGORYTHM_BYTES + DIGEST_BYTES;
+
+ @Override
+ public CryptoAlgorithm getAlgorithm() {
+ return JNIRIPEMD160;
+ }
+
+ @Override
+ public HashDigest hash(byte[] data) {
+
+ if (data == null)
+ throw new IllegalArgumentException("This data is null!");
+
+ JNIRIPEMD160Utils ripemd160 = new JNIRIPEMD160Utils();
+ byte[] digestBytes = ripemd160.hash(data);
+ return new HashDigest(JNIRIPEMD160, digestBytes);
+ }
+
+ @Override
+ public boolean verify(HashDigest digest, byte[] data) {
+ HashDigest hashDigest = hash(data);
+ return Arrays.equals(hashDigest.toBytes(), digest.toBytes());
+ }
+
+ @Override
+ public boolean supportHashDigest(byte[] digestBytes) {
+ // 验证输入字节数组长度=算法标识长度+摘要长度,字节数组的算法标识对应JNIRIPEMD160算法
+ return digestBytes.length == DIGEST_LENGTH && JNIRIPEMD160.CODE == digestBytes[0];
+ }
+
+ @Override
+ public HashDigest resolveHashDigest(byte[] digestBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new HashDigest(digestBytes);
+ }
+}
+
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/hash/JNISHA256HashFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/hash/JNISHA256HashFunction.java
new file mode 100644
index 00000000..bb6e84f0
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/hash/JNISHA256HashFunction.java
@@ -0,0 +1,53 @@
+package com.jd.blockchain.crypto.impl.jni.hash;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.crypto.hash.HashFunction;
+import com.jd.blockchain.crypto.jniutils.hash.JNISHA256Utils;
+
+import java.util.Arrays;
+
+import static com.jd.blockchain.crypto.CryptoAlgorithm.JNISHA256;
+import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES;
+
+public class JNISHA256HashFunction implements HashFunction {
+
+ private static final int DIGEST_BYTES = 256/8;
+
+ private static final int DIGEST_LENGTH = ALGORYTHM_BYTES + DIGEST_BYTES;
+
+ @Override
+ public CryptoAlgorithm getAlgorithm() {
+ return JNISHA256;
+ }
+
+ @Override
+ public HashDigest hash(byte[] data) {
+
+ if (data == null)
+ throw new IllegalArgumentException("This data is null!");
+
+ JNISHA256Utils sha256 = new JNISHA256Utils();
+ byte[] digestBytes = sha256.hash(data);
+ return new HashDigest(JNISHA256,digestBytes);
+ }
+
+ @Override
+ public boolean verify(HashDigest digest, byte[] data) {
+ HashDigest hashDigest=hash(data);
+ return Arrays.equals(hashDigest.toBytes(),digest.toBytes());
+ }
+
+ @Override
+ public boolean supportHashDigest(byte[] digestBytes) {
+ // 验证输入字节数组长度=算法标识长度+摘要长度,字节数组的算法标识对应JNISHA256算法
+ return digestBytes.length == DIGEST_LENGTH && JNISHA256.CODE == digestBytes[0];
+ }
+
+ @Override
+ public HashDigest resolveHashDigest(byte[] hashDigestBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new HashDigest(hashDigestBytes);
+ }
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/asymmetric/SM2CryptoFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/asymmetric/SM2CryptoFunction.java
new file mode 100644
index 00000000..070bae5d
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/asymmetric/SM2CryptoFunction.java
@@ -0,0 +1,184 @@
+package com.jd.blockchain.crypto.impl.sm.asymmetric;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.asymmetric.*;
+import com.jd.blockchain.crypto.smutils.asymmetric.SM2Utils;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.util.encoders.Hex;
+
+import static com.jd.blockchain.crypto.CryptoAlgorithm.SM2;
+import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES;
+import static com.jd.blockchain.crypto.CryptoKeyType.PRIV_KEY;
+import static com.jd.blockchain.crypto.CryptoKeyType.PUB_KEY;
+import static com.jd.blockchain.crypto.base.BaseCryptoKey.KEY_TYPE_BYTES;
+
+public class SM2CryptoFunction implements AsymmetricEncryptionFunction, SignatureFunction {
+
+ private static final int ECPOINT_SIZE = 65;
+ private static final int PRIVKEY_SIZE = 32;
+ private static final int SIGNATUREDIGEST_SIZE = 64;
+ private static final int HASHDIGEST_SIZE = 32;
+
+ private static final int PUBKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + ECPOINT_SIZE;
+ private static final int PRIVKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + PRIVKEY_SIZE;
+ private static final int SIGNATUREDIGEST_LENGTH = ALGORYTHM_BYTES + SIGNATUREDIGEST_SIZE;
+
+ @Override
+ public Ciphertext encrypt(PubKey pubKey, byte[] data) {
+
+ byte[] rawPubKeyBytes = pubKey.getRawKeyBytes();
+
+ // 验证原始公钥长度为65字节
+ if (rawPubKeyBytes.length != ECPOINT_SIZE)
+ throw new IllegalArgumentException("This key has wrong format!");
+
+ // 验证密钥数据的算法标识对应SM2算法
+ if (pubKey.getAlgorithm() != SM2)
+ throw new IllegalArgumentException("The is not sm2 public key!");
+
+ // 调用SM2加密算法计算密文
+ return new AsymmetricCiphertext(SM2, SM2Utils.encrypt(data, rawPubKeyBytes));
+ }
+
+ @Override
+ public byte[] decrypt(PrivKey privKey, Ciphertext ciphertext) {
+
+ byte[] rawPrivKeyBytes = privKey.getRawKeyBytes();
+ byte[] rawCiphertextBytes = ciphertext.getRawCiphertext();
+
+ // 验证原始私钥长度为32字节
+ if (rawPrivKeyBytes.length != PRIVKEY_SIZE)
+ throw new IllegalArgumentException("This key has wrong format!");
+
+ // 验证密钥数据的算法标识对应SM2算法
+ if (privKey.getAlgorithm() != SM2)
+ throw new IllegalArgumentException("This key is not SM2 private key!");
+
+ // 验证密文数据的算法标识对应SM2签名算法,并且原始摘要长度为64字节
+ if (ciphertext.getAlgorithm() != SM2 || rawCiphertextBytes.length < ECPOINT_SIZE + HASHDIGEST_SIZE)
+ throw new IllegalArgumentException("This is not SM2 ciphertext!");
+
+ // 调用SM2解密算法得到明文结果
+ return SM2Utils.decrypt(rawCiphertextBytes,rawPrivKeyBytes);
+ }
+
+ @Override
+ public boolean supportPrivKey(byte[] privKeyBytes) {
+ // 验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,密钥数据的算法标识对应SM2算法,并且密钥类型是私钥
+ return privKeyBytes.length == PRIVKEY_LENGTH && privKeyBytes[0] == SM2.CODE && privKeyBytes[1] == PRIV_KEY.CODE;
+ }
+
+ @Override
+ public PrivKey resolvePrivKey(byte[] privKeyBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new PrivKey(privKeyBytes);
+ }
+
+ @Override
+ public boolean supportPubKey(byte[] pubKeyBytes) {
+ // 验证输入字节数组长度=算法标识长度+密钥类型长度+椭圆曲线点长度,密钥数据的算法标识对应SM2算法,并且密钥类型是公钥
+ return pubKeyBytes.length == PUBKEY_LENGTH && pubKeyBytes[0] == SM2.CODE && pubKeyBytes[1] == PUB_KEY.CODE;
+ }
+
+ @Override
+ public PubKey resolvePubKey(byte[] pubKeyBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new PubKey(pubKeyBytes);
+ }
+
+ @Override
+ public boolean supportCiphertext(byte[] ciphertextBytes) {
+ // 验证输入字节数组长度>=算法标识长度+椭圆曲线点长度+哈希长度,字节数组的算法标识对应SM2算法
+ return ciphertextBytes.length >= ALGORYTHM_BYTES + ECPOINT_SIZE + HASHDIGEST_SIZE && ciphertextBytes[0] == SM2.CODE;
+ }
+
+ @Override
+ public AsymmetricCiphertext resolveCiphertext(byte[] ciphertextBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new AsymmetricCiphertext(ciphertextBytes);
+ }
+
+ @Override
+ public SignatureDigest sign(PrivKey privKey, byte[] data) {
+
+ byte[] rawPrivKeyBytes = privKey.getRawKeyBytes();
+
+ // 验证原始私钥长度为256比特,即32字节
+ if (rawPrivKeyBytes.length != PRIVKEY_SIZE)
+ throw new IllegalArgumentException("This key has wrong format!");
+
+ // 验证密钥数据的算法标识对应SM2签名算法
+ if (privKey.getAlgorithm() != SM2)
+ throw new IllegalArgumentException("This key is not SM2 private key!");
+
+ // 调用SM2签名算法计算签名结果
+ return new SignatureDigest(SM2, SM2Utils.sign(data,rawPrivKeyBytes));
+ }
+
+ @Override
+ public boolean verify(SignatureDigest digest, PubKey pubKey, byte[] data) {
+
+ byte[] rawPubKeyBytes = pubKey.getRawKeyBytes();
+ byte[] rawDigestBytes = digest.getRawDigest();
+
+ // 验证原始公钥长度为520比特,即65字节
+ if (rawPubKeyBytes.length != ECPOINT_SIZE)
+ throw new IllegalArgumentException("This key has wrong format!");
+
+ // 验证密钥数据的算法标识对应SM2签名算法
+ if (pubKey.getAlgorithm() != SM2)
+ throw new IllegalArgumentException("This key is not SM2 public key!");
+
+ // 验证签名数据的算法标识对应SM2签名算法,并且原始签名长度为64字节
+ if (digest.getAlgorithm() != SM2 || rawDigestBytes.length != SIGNATUREDIGEST_SIZE)
+ throw new IllegalArgumentException("This is not SM2 signature digest!");
+
+ // 调用SM2验签算法验证签名结果
+ return SM2Utils.verify(data, rawPubKeyBytes, rawDigestBytes);
+ }
+
+ @Override
+ public boolean supportDigest(byte[] digestBytes) {
+ // 验证输入字节数组长度=算法标识长度+签名长度,字节数组的算法标识对应SM2算法
+ return digestBytes.length == SIGNATUREDIGEST_LENGTH && digestBytes[0] == SM2.CODE;
+ }
+
+ @Override
+ public SignatureDigest resolveDigest(byte[] digestBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new SignatureDigest(digestBytes);
+ }
+
+ @Override
+ public CryptoAlgorithm getAlgorithm() {
+ return SM2;
+ }
+
+ @Override
+ public CryptoKeyPair generateKeyPair() {
+
+ // 调用SM2算法的密钥生成算法生成公私钥对priKey和pubKey,返回密钥对
+ AsymmetricCipherKeyPair keyPair = SM2Utils.generateKeyPair();
+ ECPrivateKeyParameters ecPriv = (ECPrivateKeyParameters) keyPair.getPrivate();
+ ECPublicKeyParameters ecPub = (ECPublicKeyParameters) keyPair.getPublic();
+
+ byte[] privKeyBytesD = ecPriv.getD().toByteArray();
+ byte[] privKeyBytes = new byte[PRIVKEY_SIZE];
+ if (privKeyBytesD.length > PRIVKEY_SIZE)
+ System.arraycopy(privKeyBytesD,privKeyBytesD.length-PRIVKEY_SIZE,privKeyBytes,0,PRIVKEY_SIZE);
+ else System.arraycopy(privKeyBytesD,0,privKeyBytes,PRIVKEY_SIZE-privKeyBytesD.length,privKeyBytesD.length);
+
+ byte[] pubKeyBytesX = ecPub.getQ().getAffineXCoord().getEncoded();
+ byte[] pubKeyBytesY = ecPub.getQ().getAffineYCoord().getEncoded();
+ byte[] pubKeyBytes = new byte[ECPOINT_SIZE];
+ System.arraycopy(Hex.decode("04"),0,pubKeyBytes,0,1);
+ System.arraycopy(pubKeyBytesX,0,pubKeyBytes,1,32);
+ System.arraycopy(pubKeyBytesY,0,pubKeyBytes,1+32,32);
+
+ return new CryptoKeyPair(new PubKey(SM2,pubKeyBytes),new PrivKey(SM2,privKeyBytes));
+ }
+}
+
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/hash/SM3HashFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/hash/SM3HashFunction.java
new file mode 100644
index 00000000..98f9a487
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/hash/SM3HashFunction.java
@@ -0,0 +1,47 @@
+package com.jd.blockchain.crypto.impl.sm.hash;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoBytes;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.crypto.hash.HashFunction;
+import com.jd.blockchain.crypto.smutils.hash.SM3Utils;
+
+import java.util.Arrays;
+
+import static com.jd.blockchain.crypto.CryptoAlgorithm.SM3;
+
+public class SM3HashFunction implements HashFunction {
+
+ private static final int DIGEST_BYTES = 256/8;
+
+ private static final int DIGEST_LENGTH = CryptoBytes.ALGORYTHM_BYTES + DIGEST_BYTES;
+
+ @Override
+ public CryptoAlgorithm getAlgorithm() {
+ return SM3;
+ }
+
+ @Override
+ public HashDigest hash(byte[] data) {
+ byte[] digestBytes = SM3Utils.hash(data);
+ return new HashDigest(SM3,digestBytes);
+ }
+
+ @Override
+ public boolean verify(HashDigest digest, byte[] data) {
+ HashDigest hashDigest = hash(data);
+ return Arrays.equals(hashDigest.toBytes(), digest.toBytes());
+ }
+
+ @Override
+ public boolean supportHashDigest(byte[] digestBytes) {
+ // 验证输入字节数组长度=算法标识长度+摘要长度,以及算法标识;
+ return SM3.CODE == digestBytes[0] && DIGEST_LENGTH == digestBytes.length;
+ }
+
+ @Override
+ public HashDigest resolveHashDigest(byte[] hashDigestBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new HashDigest(hashDigestBytes);
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/symmetric/SM4SymmetricEncryptionFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/symmetric/SM4SymmetricEncryptionFunction.java
new file mode 100644
index 00000000..3bba6efa
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/symmetric/SM4SymmetricEncryptionFunction.java
@@ -0,0 +1,145 @@
+package com.jd.blockchain.crypto.impl.sm.symmetric;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoKey;
+import com.jd.blockchain.crypto.smutils.symmetric.SM4Utils;
+import com.jd.blockchain.crypto.symmetric.SymmetricCiphertext;
+import com.jd.blockchain.crypto.symmetric.SymmetricEncryptionFunction;
+import com.jd.blockchain.crypto.symmetric.SymmetricKey;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import static com.jd.blockchain.crypto.CryptoAlgorithm.SM4;
+import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES;
+import static com.jd.blockchain.crypto.CryptoKeyType.SYMMETRIC_KEY;
+import static com.jd.blockchain.crypto.base.BaseCryptoKey.KEY_TYPE_BYTES;
+
+public class SM4SymmetricEncryptionFunction implements SymmetricEncryptionFunction {
+
+ private static final int KEY_SIZE = 16;
+ private static final int BLOCK_SIZE = 16;
+
+ private static final int SYMMETRICKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + KEY_SIZE;
+
+ @Override
+ public Ciphertext encrypt(SymmetricKey key, byte[] data) {
+
+ byte[] rawKeyBytes = key.getRawKeyBytes();
+
+ // 验证原始密钥长度为128比特,即16字节
+ if (rawKeyBytes.length != KEY_SIZE)
+ throw new IllegalArgumentException("This key has wrong format!");
+
+ // 验证密钥数据的算法标识对应SM4算法
+ if (key.getAlgorithm() != SM4)
+ throw new IllegalArgumentException("The is not SM4 symmetric key!");
+
+ // 调用底层SM4算法并计算密文数据
+ return new SymmetricCiphertext(SM4, SM4Utils.encrypt(data, rawKeyBytes));
+ }
+
+ @Override
+ public void encrypt(SymmetricKey key, InputStream in, OutputStream out) {
+
+ // 读输入流得到明文,加密,密文数据写入输出流
+ try {
+ byte[] sm4Data = new byte[in.available()];
+ in.read(sm4Data);
+ in.close();
+
+ out.write(encrypt(key, sm4Data).toBytes());
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public byte[] decrypt(SymmetricKey key, Ciphertext ciphertext) {
+
+ byte[] rawKeyBytes = key.getRawKeyBytes();
+ byte[] rawCiphertextBytes = ciphertext.getRawCiphertext();
+
+ // 验证原始密钥长度为128比特,即16字节
+ if (rawKeyBytes.length != KEY_SIZE)
+ throw new IllegalArgumentException("This key has wrong format!");
+
+ // 验证密钥数据的算法标识对应SM4算法
+ if (key.getAlgorithm().CODE != SM4.CODE)
+ throw new IllegalArgumentException("The is not SM4 symmetric key!");
+
+ // 验证原始密文长度为分组长度的整数倍
+ if (rawCiphertextBytes.length % BLOCK_SIZE != 0)
+ throw new IllegalArgumentException("This ciphertext has wrong format!");
+
+ // 验证密文数据算法标识对应SM4算法
+ if (ciphertext.getAlgorithm() != SM4)
+ throw new IllegalArgumentException("This is not SM4 ciphertext!");
+
+ // 调用底层SM4算法解密,得到明文
+ try {
+ return SM4Utils.decrypt(rawCiphertextBytes, rawKeyBytes);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ throw new IllegalArgumentException("Decrypting process fails!");
+ }
+
+ @Override
+ public void decrypt(SymmetricKey key, InputStream in, OutputStream out) {
+
+ // 读输入流得到密文数据,解密,明文写入输出流
+ try {
+ byte[] sm4Data = new byte[in.available()];
+ in.read(sm4Data);
+ in.close();
+
+ if (!supportCiphertext(sm4Data))
+ throw new IllegalArgumentException("InputStream is not valid SM4 ciphertext!");
+
+ out.write(decrypt(key, resolveCiphertext(sm4Data)));
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public boolean supportSymmetricKey(byte[] symmetricKeyBytes) {
+ //验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,字节数组的算法标识对应SM4算法且密钥密钥类型是对称密钥
+ return symmetricKeyBytes.length == SYMMETRICKEY_LENGTH && symmetricKeyBytes[0] == SM4.CODE && symmetricKeyBytes[1] == SYMMETRIC_KEY.CODE;
+ }
+
+ @Override
+ public SymmetricKey resolveSymmetricKey(byte[] symmetricKeyBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new SymmetricKey(symmetricKeyBytes);
+ }
+
+ @Override
+ public boolean supportCiphertext(byte[] ciphertextBytes) {
+ // 验证(输入字节数组长度-算法标识长度)是分组长度的整数倍,字节数组的算法标识对应SM4算法
+ return (ciphertextBytes.length - ALGORYTHM_BYTES) % BLOCK_SIZE == 0 && ciphertextBytes[0] == SM4.CODE;
+ }
+
+ @Override
+ public SymmetricCiphertext resolveCiphertext(byte[] ciphertextBytes) {
+ // 由框架调用 support 方法检查有效性,在此不做重复检查;
+ return new SymmetricCiphertext(ciphertextBytes);
+ }
+
+ @Override
+ public CryptoAlgorithm getAlgorithm() {
+ return SM4;
+ }
+
+ @Override
+ public CryptoKey generateSymmetricKey() {
+ // 根据对应的标识和原始密钥生成相应的密钥数据
+ return new SymmetricKey(SM4, SM4Utils.generateKey());
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/asymmetric/JNIED25519Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/asymmetric/JNIED25519Utils.java
new file mode 100644
index 00000000..e1b4bb3b
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/asymmetric/JNIED25519Utils.java
@@ -0,0 +1,37 @@
+package com.jd.blockchain.crypto.jniutils.asymmetric;
+
+
+import com.jd.blockchain.crypto.jniutils.hash.JNISHA256Utils;
+
+import java.util.Objects;
+
+public class JNIED25519Utils {
+
+ /* load c library */
+ static {
+ //differentiate OS
+ String osName = System.getProperty("os.name").toLowerCase();
+ String path="";
+ // Windows OS
+ if (osName.startsWith("windows")){
+ path = Objects.requireNonNull(JNIED25519Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/asymmetric/c_ed25519.dll")).getPath();
+ }
+ // Linux OS
+ else if (osName.contains("linux")){
+ path = Objects.requireNonNull(JNIED25519Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/asymmetric/libc_ed25519.so")).getPath();
+ }
+ // Mac OS
+ else if (osName.contains("mac")){
+ path = Objects.requireNonNull(JNISHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/asymmetric/libc_ed25519.jnilib")).getPath();
+ }
+
+ System.load(path);
+ }
+
+ /* define java native method */
+ public native void generateKeyPair(byte[] privKey, byte[] pubKey);
+ public native byte[] getPubKey(byte[] privKey);
+ public native byte[] sign(byte[] msg, byte[] privKey, byte[] pubKey);
+ public native boolean verify(byte[] msg, byte[] pubKey, byte[] signature);
+ }
+
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNIMBSHA256Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNIMBSHA256Utils.java
new file mode 100644
index 00000000..5518117d
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNIMBSHA256Utils.java
@@ -0,0 +1,24 @@
+package com.jd.blockchain.crypto.jniutils.hash;
+
+import java.util.Objects;
+
+public class JNIMBSHA256Utils {
+ /* load c library */
+ static{
+ //differentiate OS
+ String osName = System.getProperty("os.name").toLowerCase();
+ String pathOfSo;
+ String pathOfSo2;
+ if (osName.contains("linux")){
+ pathOfSo = Objects.requireNonNull(JNIMBSHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/libc_mbsha256.so")).getPath();
+ pathOfSo2 = Objects.requireNonNull(JNIMBSHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/libisal_crypto.so.2")).getPath();
+ }
+ else throw new IllegalArgumentException("The JNIMBSHA256 implementation is not supported in this Operation System!");
+
+ System.load(pathOfSo2);
+ System.load(pathOfSo);
+ }
+
+ /* define java native method */
+ public native byte[][] multiBufferHash(byte[][] multiMsgs);
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNIRIPEMD160Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNIRIPEMD160Utils.java
new file mode 100644
index 00000000..d9e0ac7a
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNIRIPEMD160Utils.java
@@ -0,0 +1,30 @@
+package com.jd.blockchain.crypto.jniutils.hash;
+
+import java.util.Objects;
+
+public class JNIRIPEMD160Utils {
+
+ /* load c library */
+ static {
+ //differentiate OS
+ String osName = System.getProperty("os.name").toLowerCase();
+ String path="";
+ // Windows OS
+ if (osName.startsWith("windows")){
+ path = Objects.requireNonNull(JNIRIPEMD160Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/c_ripemd160.dll")).getPath();
+ }
+ // Linux OS
+ else if (osName.contains("linux")){
+ path = Objects.requireNonNull(JNIRIPEMD160Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/libc_ripemd160.so")).getPath();
+ }
+ // Mac OS
+ else if (osName.contains("mac")){
+ path = Objects.requireNonNull(JNISHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/libc_ripemd160.jnilib")).getPath();
+ }
+
+ System.load(path);
+ }
+
+ /* define java native method */
+ public native byte[] hash(byte[] msg);
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNISHA256Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNISHA256Utils.java
new file mode 100644
index 00000000..a26ef912
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNISHA256Utils.java
@@ -0,0 +1,30 @@
+package com.jd.blockchain.crypto.jniutils.hash;
+
+import java.util.Objects;
+
+public class JNISHA256Utils {
+
+ /* load c library */
+ static {
+ //differentiate OS
+ String osName = System.getProperty("os.name").toLowerCase();
+ String path="";
+ // Windows OS
+ if (osName.startsWith("windows")){
+ path = Objects.requireNonNull(JNISHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/c_sha256.dll")).getPath();
+ }
+ // Linux OS
+ else if (osName.contains("linux")){
+ path = Objects.requireNonNull(JNISHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/libc_sha256.so")).getPath();
+ }
+ // Mac OS
+ else if (osName.contains("mac")){
+ path = Objects.requireNonNull(JNISHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/libc_sha256.jnilib")).getPath();
+ }
+
+ System.load(path);
+ }
+
+ /* define java native method */
+ public native byte[] hash(byte[] msg);
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/asymmetric/SM2Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/asymmetric/SM2Utils.java
new file mode 100644
index 00000000..8dfa598f
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/asymmetric/SM2Utils.java
@@ -0,0 +1,281 @@
+package com.jd.blockchain.crypto.smutils.asymmetric;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.engines.SM2Engine;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.*;
+import org.bouncycastle.crypto.signers.SM2Signer;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+public class SM2Utils {
+
+ private static final int COORDS_SIZE = 32;
+ private static final int POINT_SIZE = COORDS_SIZE * 2 + 1;
+ private static final int R_SIZE =32;
+ private static final int S_SIZE =32;
+
+ // The length of sm3 output is 32 bytes
+ private static final int SM3DIGEST_LENGTH = 32;
+
+ private static final BigInteger SM2_ECC_P = new BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16);
+ private static final BigInteger SM2_ECC_A = new BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16);
+ private static final BigInteger SM2_ECC_B = new BigInteger("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16);
+ private static final BigInteger SM2_ECC_N = new BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16);
+ private static final BigInteger SM2_ECC_GX = new BigInteger("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
+ private static final BigInteger SM2_ECC_GY = new BigInteger("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);
+
+ // To get the curve from the equation y^2=x^3+ax+b according the coefficient a and b,
+ // with the big prime p, and obtain the generator g and the domain's parameters
+ private static final ECCurve curve = new ECCurve.Fp(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B);
+ private static final ECPoint g = curve.createPoint(SM2_ECC_GX, SM2_ECC_GY);
+ private static final ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N);
+
+
+ //-----------------Key Pair Generation Algorithm-----------------
+
+ /**
+ * key generation
+ *
+ * @return key pair
+ */
+ public static AsymmetricCipherKeyPair generateKeyPair(){
+
+ SecureRandom random = new SecureRandom();
+ return generateKeyPair(random);
+ }
+
+ public static AsymmetricCipherKeyPair generateKeyPair(SecureRandom random){
+
+ ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParams,random);
+ ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+
+ // To generate the key pair
+ keyPairGenerator.init(keyGenerationParams);
+ return keyPairGenerator.generateKeyPair();
+ }
+
+
+ //-----------------Digital Signature Algorithm-----------------
+
+
+ /**
+ * signature generation
+ *
+ * @param data data to be signed
+ * @param privateKey private key
+ * @return signature
+ */
+ public static byte[] sign(byte[] data, byte[] privateKey){
+
+ SecureRandom random = new SecureRandom();
+ ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1,privateKey),domainParams);
+ CipherParameters param = new ParametersWithRandom(privKey,random);
+
+ return sign(data,param);
+ }
+
+ public static byte[] sign(byte[] data, byte[] privateKey, SecureRandom random, String ID){
+
+ ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1,privateKey),domainParams);
+ CipherParameters param = new ParametersWithID(new ParametersWithRandom(privKey,random),ID.getBytes());
+
+ return sign(data,param);
+ }
+
+ private static byte[] sign(byte[] data, CipherParameters param){
+
+ SM2Signer signer = new SM2Signer();
+
+ // To get Z_A and prepare parameters
+ signer.init(true,param);
+ // To fill the whole message to be signed
+ signer.update(data,0,data.length);
+ // To get and return the signature result;
+
+ byte[] encodedSignature;
+ try {
+ encodedSignature = signer.generateSignature();
+ } catch (CryptoException e) {
+ throw new com.jd.blockchain.crypto.CryptoException(e.getMessage(), e);
+ }
+
+ // To decode the signature
+ ASN1Sequence sig = ASN1Sequence.getInstance(encodedSignature);
+ byte[] rBytes = BigIntegerTo32Bytes(ASN1Integer.getInstance(sig.getObjectAt(0)).getValue());
+ byte[] sBytes = BigIntegerTo32Bytes(ASN1Integer.getInstance(sig.getObjectAt(1)).getValue());
+
+ byte[] signature = new byte[R_SIZE + S_SIZE];
+ System.arraycopy(rBytes,0,signature,0,R_SIZE);
+ System.arraycopy(sBytes,0,signature,R_SIZE,S_SIZE);
+
+ return signature;
+ }
+
+ /**
+ * verification
+ *
+ * @param data data to be signed
+ * @param publicKey public key
+ * @return true or false
+ */
+ public static boolean verify(byte[] data, byte[] publicKey, byte[] signature){
+
+ ECPoint pubKeyPoint = resolvePubKeyBytes(publicKey);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubKeyPoint,domainParams);
+
+ return verify(data,pubKey,signature);
+ }
+
+ public static boolean verify(byte[] data, byte[] publicKey, byte[] signature, String ID){
+
+ ECPoint pubKeyPoint = resolvePubKeyBytes(publicKey);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubKeyPoint,domainParams);
+ ParametersWithID param = new ParametersWithID(pubKey,ID.getBytes());
+
+ return verify(data,param,signature);
+ }
+
+ private static boolean verify(byte[] data, CipherParameters param, byte[] signature){
+
+
+ SM2Signer verifier = new SM2Signer();
+
+ // To get Z_A and prepare parameters
+ verifier.init(false,param);
+ // To fill the whole message
+ verifier.update(data,0,data.length);
+ // To verify the signature
+
+ byte[] rBytes = new byte[R_SIZE];
+ byte[] sBytes = new byte[S_SIZE];
+ System.arraycopy(signature,0,rBytes,0,R_SIZE);
+ System.arraycopy(signature,R_SIZE,sBytes,0,S_SIZE);
+
+ BigInteger r = new BigInteger(1,rBytes);
+ BigInteger s = new BigInteger(1,sBytes);
+ byte[] encodedSignature = new byte[0];
+ try {
+ encodedSignature = new DERSequence(new ASN1Encodable[] { new ASN1Integer(r), new ASN1Integer(s)}).getEncoded();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return verifier.verifySignature(encodedSignature);
+ }
+
+
+
+
+ //-----------------Public Key Encryption Algorithm-----------------
+
+ /**
+ * encryption
+ *
+ * @param plainBytes plaintext
+ * @param publicKey public key
+ * @return ciphertext
+ */
+ public static byte[] encrypt(byte[] plainBytes, byte[] publicKey){
+
+ SecureRandom random = new SecureRandom();
+ return encrypt(plainBytes,publicKey,random);
+ }
+
+ public static byte[] encrypt(byte[] plainBytes, byte[] publicKey, SecureRandom random){
+
+ ECPoint pubKeyPoint = resolvePubKeyBytes(publicKey);
+ ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubKeyPoint,domainParams);
+ ParametersWithRandom param = new ParametersWithRandom(pubKey,random);
+
+ SM2Engine encryptor = new SM2Engine();
+
+ // To prepare parameters
+ encryptor.init(true,param);
+
+ // To generate the twisted ciphertext c1c2c3.
+ // The latest standard specification indicates that the correct ordering is c1c3c2
+ byte[] c1c2c3 = new byte[0];
+ try {
+ c1c2c3 = encryptor.processBlock(plainBytes,0,plainBytes.length);
+ } catch (InvalidCipherTextException e) {
+ e.printStackTrace();
+ }
+
+ // get correct output c1c3c2 from c1c2c3
+ byte[] c1c3c2 = new byte[c1c2c3.length];
+ System.arraycopy(c1c2c3,0,c1c3c2,0,POINT_SIZE);
+ System.arraycopy(c1c2c3,POINT_SIZE,c1c3c2,POINT_SIZE + SM3DIGEST_LENGTH, plainBytes.length);
+ System.arraycopy(c1c2c3,POINT_SIZE + plainBytes.length, c1c3c2,POINT_SIZE,SM3DIGEST_LENGTH);
+
+ return c1c3c2;
+ }
+
+ /**
+ * decryption
+ *
+ * @param cipherBytes ciphertext
+ * @param privateKey private key
+ * @return plaintext
+ */
+ public static byte[] decrypt(byte[] cipherBytes, byte[] privateKey){
+
+
+ // To get c1c2c3 from ciphertext whose ordering is c1c3c2
+ byte[] c1c2c3 = new byte[cipherBytes.length];
+ System.arraycopy(cipherBytes,0,c1c2c3,0,POINT_SIZE);
+ System.arraycopy(cipherBytes,POINT_SIZE,c1c2c3,c1c2c3.length-SM3DIGEST_LENGTH, SM3DIGEST_LENGTH);
+ System.arraycopy(cipherBytes,SM3DIGEST_LENGTH + POINT_SIZE,c1c2c3,POINT_SIZE,c1c2c3.length-SM3DIGEST_LENGTH-POINT_SIZE);
+
+ ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1,privateKey),domainParams);
+
+ SM2Engine decryptor = new SM2Engine();
+
+ // To prepare parameters
+ decryptor.init(false,privKey);
+
+ // To output the plaintext
+ try {
+ return decryptor.processBlock(c1c2c3,0,c1c2c3.length);
+ } catch (InvalidCipherTextException e) {
+ throw new com.jd.blockchain.crypto.CryptoException(e.getMessage(), e);
+ }
+ }
+
+ // To convert BigInteger to byte[] whose length is 32
+ private static byte[] BigIntegerTo32Bytes(BigInteger b){
+ byte[] tmp = b.toByteArray();
+ byte[] result = new byte[32];
+ if (tmp.length > result.length)
+ System.arraycopy(tmp, tmp.length-result.length, result, 0, result.length);
+ else System.arraycopy(tmp,0,result,result.length-tmp.length,tmp.length);
+ return result;
+ }
+
+ // To retrieve the public key point from publicKey in byte array mode
+ private static ECPoint resolvePubKeyBytes(byte[] publicKey){
+
+ byte[] pubKeyX = new byte[COORDS_SIZE];
+ byte[] pubKeyY = new byte[COORDS_SIZE];
+ System.arraycopy(publicKey,1,pubKeyX,0,COORDS_SIZE);
+ System.arraycopy(publicKey,1+COORDS_SIZE,pubKeyY,0,COORDS_SIZE);
+
+ return curve.createPoint(new BigInteger(1,pubKeyX), new BigInteger(1,pubKeyY));
+ }
+ public static ECCurve getCurve(){return curve;}
+ public static ECDomainParameters getDomainParams(){return domainParams;}
+}
+
+
+
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/hash/SM3Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/hash/SM3Utils.java
new file mode 100644
index 00000000..788247f3
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/hash/SM3Utils.java
@@ -0,0 +1,25 @@
+package com.jd.blockchain.crypto.smutils.hash;
+
+import org.bouncycastle.crypto.digests.SM3Digest;
+
+public class SM3Utils {
+
+
+ // The length of sm3 output is 32 bytes
+ private static final int SM3DIGEST_LENGTH = 32;
+
+ public static byte[] hash(byte[] data) {
+
+ byte[] result = new byte[SM3DIGEST_LENGTH];
+
+ SM3Digest sm3digest = new SM3Digest();
+
+ sm3digest.update(data, 0, data.length);
+ sm3digest.doFinal(result, 0);
+
+ return result;
+
+ }
+
+}
+
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/symmetric/SM4Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/symmetric/SM4Utils.java
new file mode 100644
index 00000000..b1855a19
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/symmetric/SM4Utils.java
@@ -0,0 +1,143 @@
+package com.jd.blockchain.crypto.smutils.symmetric;
+
+import com.jd.blockchain.crypto.CryptoException;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.engines.SM4Engine;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.paddings.PKCS7Padding;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+
+import java.security.SecureRandom;
+
+public class SM4Utils {
+
+ // SM4 supports 128-bit secret key
+ private static final int KEY_Length = 128;
+ // One block contains 16 bytes
+ private static final int BLOCK_SIZE = 16;
+ // Initial vector's size is 16 bytes
+ private static final int IV_SIZE = 16;
+
+
+ /**
+ * key generation
+ *
+ * @return secret key
+ */
+ public static byte[] generateKey(){
+
+ CipherKeyGenerator keyGenerator = new CipherKeyGenerator();
+
+ // To provide secure randomness and key length as input
+ // to prepare generate private key
+ keyGenerator.init(new KeyGenerationParameters(new SecureRandom(),KEY_Length));
+
+ // To generate key
+ return keyGenerator.generateKey();
+ }
+
+
+ /**
+ * encryption
+ *
+ * @param plainBytes plaintext
+ * @param secretKey symmetric key
+ * @param iv initial vector
+ * @return ciphertext
+ */
+ public static byte[] encrypt(byte[] plainBytes, byte[] secretKey, byte[] iv){
+
+ // To ensure that plaintext is not null
+ if (plainBytes == null)
+ {
+ throw new IllegalArgumentException("plaintext is null!");
+ }
+
+ // To get the value padded into input
+ int padding = 16 - plainBytes.length % BLOCK_SIZE;
+ // The plaintext with padding value
+ byte[] plainBytesWithPadding = new byte[plainBytes.length + padding];
+ System.arraycopy(plainBytes,0,plainBytesWithPadding,0,plainBytes.length);
+ // The padder adds PKCS7 padding to the input, which makes its length to
+ // become an integral multiple of 16 bytes
+ PKCS7Padding padder = new PKCS7Padding();
+ // To add padding
+ padder.addPadding(plainBytesWithPadding, plainBytes.length);
+
+ CBCBlockCipher encryptor = new CBCBlockCipher(new SM4Engine());
+ // To provide key and initialisation vector as input
+ encryptor.init(true,new ParametersWithIV(new KeyParameter(secretKey),iv));
+ byte[] output = new byte[plainBytesWithPadding.length + IV_SIZE];
+ // To encrypt the input_p in CBC mode
+ for(int i = 0 ; i < plainBytesWithPadding.length/BLOCK_SIZE; i++)
+ encryptor.processBlock(plainBytesWithPadding, i * BLOCK_SIZE, output, (i+1) * BLOCK_SIZE);
+
+ // The IV locates on the first block of ciphertext
+ System.arraycopy(iv,0,output,0,BLOCK_SIZE);
+ return output;
+ }
+
+ public static byte[] encrypt(byte[] plainBytes, byte[] secretKey){
+
+ byte[] iv = new byte[IV_SIZE];
+ SecureRandom random = new SecureRandom();
+ random.nextBytes(iv);
+ return encrypt(plainBytes,secretKey,iv);
+ }
+
+ /**
+ * decryption
+ *
+ * @param cipherBytes ciphertext
+ * @param secretKey symmetric key
+ * @return plaintext
+ */
+ public static byte[] decrypt(byte[] cipherBytes, byte[] secretKey){
+
+ // To ensure that the ciphertext is not null
+ if (cipherBytes == null)
+ {
+ throw new IllegalArgumentException("ciphertext is null!");
+ }
+
+ // To ensure that the ciphertext's length is integral multiples of 16 bytes
+ if ( cipherBytes.length % BLOCK_SIZE != 0 )
+ {
+ throw new IllegalArgumentException("ciphertext's length is wrong!");
+ }
+
+ byte[] iv = new byte[IV_SIZE];
+ System.arraycopy(cipherBytes,0,iv,0,BLOCK_SIZE);
+
+ CBCBlockCipher decryptor = new CBCBlockCipher(new SM4Engine());
+ // To prepare the decryption
+ decryptor.init(false,new ParametersWithIV(new KeyParameter(secretKey),iv));
+ byte[] outputWithPadding = new byte[cipherBytes.length-BLOCK_SIZE];
+ // To decrypt the input in CBC mode
+ for(int i = 1 ; i < cipherBytes.length/BLOCK_SIZE ; i++)
+ decryptor.processBlock(cipherBytes, i * BLOCK_SIZE, outputWithPadding, (i-1) * BLOCK_SIZE);
+
+ int p = outputWithPadding[outputWithPadding.length-1];
+ // To ensure that the padding of output_p is valid
+ if(p > BLOCK_SIZE || p < 0x01)
+ {
+ throw new CryptoException("There no exists such padding!");
+
+ }
+ for(int i = 0 ; i < p ; i++)
+ {
+ if(outputWithPadding[outputWithPadding.length-i-1] != p)
+ {
+ throw new CryptoException("Padding is invalid!");
+ }
+ }
+
+ // To remove the padding from output and obtain plaintext
+ byte[] output = new byte[outputWithPadding.length-p];
+ System.arraycopy(outputWithPadding, 0, output, 0, output.length);
+ return output;
+ }
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricCiphertext.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricCiphertext.java
new file mode 100644
index 00000000..39878e38
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricCiphertext.java
@@ -0,0 +1,34 @@
+package com.jd.blockchain.crypto.symmetric;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.base.BaseCryptoBytes;
+
+public class SymmetricCiphertext extends BaseCryptoBytes implements Ciphertext {
+
+ public SymmetricCiphertext(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) {
+ super(algorithm, rawCryptoBytes);
+ }
+
+ public SymmetricCiphertext(byte[] cryptoBytes) {
+ super(cryptoBytes);
+ }
+
+// @Override
+// protected void support(CryptoAlgorithm algorithm) {
+// if (!algorithm.isSymmetric()) {
+// throw new CryptoException("SymmetricCiphertext doesn't support algorithm[" + algorithm + "]!");
+// }
+// }
+
+ @Override
+ protected boolean support(CryptoAlgorithm algorithm) {
+ return algorithm.isSymmetric();
+ }
+
+ @Override
+ public byte[] getRawCiphertext() {
+ return getRawCryptoBytes().getBytesCopy();
+ }
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricCryptography.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricCryptography.java
new file mode 100644
index 00000000..4de371a2
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricCryptography.java
@@ -0,0 +1,34 @@
+package com.jd.blockchain.crypto.symmetric;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+
+public interface SymmetricCryptography {
+
+ /**
+ * 生成秘钥对;
+ *
+ * @param algorithm
+ * @return
+ */
+ SymmetricKey generateKey(CryptoAlgorithm algorithm);
+
+ /**
+ * 获取签名方法;
+ *
+ * @param algorithm
+ * @return
+ */
+ SymmetricEncryptionFunction getSymmetricEncryptionFunction(CryptoAlgorithm algorithm);
+
+ byte[] decrypt(byte[] symmetricKeyBytes,byte[] ciphertextBytes);
+
+ Ciphertext resolveCiphertext(byte[] ciphertextBytes);
+
+ Ciphertext tryResolveCiphertext(byte[] ciphertextBytes);
+
+ SymmetricKey resolveSymmetricKey(byte[] symmetricKeyBytes);
+
+ SymmetricKey tryResolveSymmetricKey(byte[] symmetricKeyBytes);
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricEncryptionFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricEncryptionFunction.java
new file mode 100644
index 00000000..5e97958c
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricEncryptionFunction.java
@@ -0,0 +1,82 @@
+package com.jd.blockchain.crypto.symmetric;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoFunction;
+import com.jd.blockchain.crypto.CryptoSymmetricKeyGenerator;
+
+public interface SymmetricEncryptionFunction extends CryptoSymmetricKeyGenerator, CryptoFunction {
+
+ /**
+ * 加密;
+ *
+ * @param key 密钥;
+ * @param data 明文;
+ * @return
+ */
+ Ciphertext encrypt(SymmetricKey key, byte[] data);
+
+ /**
+ * 加密明文的输入流,把密文写入输出流;
+ *
+ * @param key 密钥;
+ * @param in 明文的输入流;
+ * @param out 密文的输出流;
+ */
+ void encrypt(SymmetricKey key, InputStream in, OutputStream out);
+
+ /**
+ * 解密;
+ *
+ * @param key 密钥;
+ * @param ciphertext 密文;
+ * @return
+ */
+ byte[] decrypt(SymmetricKey key, Ciphertext ciphertext);
+
+ /**
+ * 解密密文的输入流,把明文写入输出流;
+ *
+ * @param key 密钥;
+ * @param in 密文的输入流;
+ * @param out 明文的输出流;
+ */
+ void decrypt(SymmetricKey key, InputStream in, OutputStream out);
+
+
+ /**
+ * 校验对称密钥格式是否满足要求;
+ *
+ * @param symmetricKeyBytes 包含算法标识、密钥掩码和对称密钥的字节数组
+ * @return 是否满足指定算法的对称密钥格式
+ */
+ boolean supportSymmetricKey(byte[] symmetricKeyBytes);
+
+ /**
+ * 将字节数组形式的密钥转换成SymmetricKey格式;
+ *
+ * @param symmetricKeyBytes 包含算法标识、密钥掩码和对称密钥的字节数组
+ * @return SymmetricKey形式的对称密钥
+ */
+ SymmetricKey resolveSymmetricKey(byte[] symmetricKeyBytes);
+
+ /**
+ * 校验密文格式是否满足要求;
+ *
+ * @param ciphertextBytes 包含算法标识和密文的字节数组
+ * @return 是否满足指定算法的密文格式
+ */
+ boolean supportCiphertext(byte[] ciphertextBytes);
+
+ /**
+ * 将字节数组形式的密文转换成SymmetricCiphertext格式;
+ *
+ * @param ciphertextBytes 包含算法标识和密文的字节数组
+ * @return SymmetricCiphertext形式的签名摘要
+ */
+ SymmetricCiphertext resolveCiphertext(byte[] ciphertextBytes);
+
+
+}
diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricKey.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricKey.java
new file mode 100644
index 00000000..c226dfd3
--- /dev/null
+++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricKey.java
@@ -0,0 +1,31 @@
+package com.jd.blockchain.crypto.symmetric;
+
+import static com.jd.blockchain.crypto.CryptoKeyType.SYMMETRIC_KEY;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoKeyType;
+import com.jd.blockchain.crypto.base.BaseCryptoKey;
+
+public class SymmetricKey extends BaseCryptoKey {
+
+ private static final long serialVersionUID = 5055547663903904933L;
+
+ public SymmetricKey(CryptoAlgorithm algorithm, byte[] rawKeyBytes) {
+ super(algorithm, rawKeyBytes, SYMMETRIC_KEY);
+ }
+
+ public SymmetricKey(byte[] keyBytes) {
+ super(keyBytes);
+ }
+
+ @Override
+ protected boolean support(CryptoKeyType keyType) {
+ return SYMMETRIC_KEY == keyType;
+ }
+
+ @Override
+ public CryptoKeyType getKeyType() {
+ return SYMMETRIC_KEY;
+ }
+
+}
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/asymmetric/AsymmtricCryptographyImplTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/asymmetric/AsymmtricCryptographyImplTest.java
new file mode 100644
index 00000000..2970635b
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/asymmetric/AsymmtricCryptographyImplTest.java
@@ -0,0 +1,900 @@
+package test.com.jd.blockchain.crypto.asymmetric;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoException;
+import com.jd.blockchain.crypto.CryptoKeyType;
+import com.jd.blockchain.crypto.asymmetric.*;
+import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+import org.junit.Test;
+
+import java.util.Random;
+
+import static com.jd.blockchain.crypto.CryptoKeyType.PRIV_KEY;
+import static com.jd.blockchain.crypto.CryptoKeyType.PUB_KEY;
+import static org.junit.Assert.*;
+import static org.junit.Assert.assertNotNull;
+
+public class AsymmtricCryptographyImplTest {
+
+ @Test
+ public void testGenerateKeyPair() {
+
+ AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl();
+
+ //test ED25519
+ CryptoAlgorithm algorithm = CryptoAlgorithm.ED25519;
+ CryptoKeyPair keyPair = asymmetricCrypto.generateKeyPair(algorithm);
+
+ assertNotNull(keyPair);
+
+ PubKey pubKey = keyPair.getPubKey();
+ PrivKey privKey = keyPair.getPrivKey();
+
+ assertNotNull(pubKey);
+ assertNotNull(privKey);
+
+ assertEquals(algorithm,pubKey.getAlgorithm());
+ assertEquals(algorithm,privKey.getAlgorithm());
+
+ assertEquals(32,pubKey.getRawKeyBytes().length);
+ assertEquals(32,privKey.getRawKeyBytes().length);
+
+ byte[] pubKeyBytes = pubKey.toBytes();
+ byte[] privKeyBytes = privKey.toBytes();
+
+ assertEquals(32+1+1,pubKeyBytes.length);
+ assertEquals(32+1+1,privKeyBytes.length);
+
+ assertEquals(CryptoAlgorithm.ED25519.CODE,pubKeyBytes[0]);
+ assertEquals(CryptoAlgorithm.ED25519.CODE,privKeyBytes[0]);
+ assertEquals(CryptoAlgorithm.ED25519, CryptoAlgorithm.valueOf(pubKey.getAlgorithm().CODE));
+ assertEquals(CryptoAlgorithm.ED25519, CryptoAlgorithm.valueOf(privKey.getAlgorithm().CODE));
+
+ assertEquals(pubKey.getKeyType().CODE,pubKeyBytes[1]);
+ assertEquals(privKey.getKeyType().CODE,privKeyBytes[1]);
+
+
+ //test SM2
+ algorithm = CryptoAlgorithm.SM2;
+ keyPair = asymmetricCrypto.generateKeyPair(algorithm);
+
+ assertNotNull(keyPair);
+
+ pubKey = keyPair.getPubKey();
+ privKey = keyPair.getPrivKey();
+
+ assertNotNull(pubKey);
+ assertNotNull(privKey);
+
+ assertEquals(algorithm,pubKey.getAlgorithm());
+ assertEquals(algorithm,privKey.getAlgorithm());
+
+ assertEquals(65,pubKey.getRawKeyBytes().length);
+ assertEquals(32,privKey.getRawKeyBytes().length);
+
+ pubKeyBytes = pubKey.toBytes();
+ privKeyBytes = privKey.toBytes();
+
+ assertEquals(32+1+1,privKeyBytes.length);
+ assertEquals(65+1+1,pubKeyBytes.length);
+
+ assertEquals(CryptoAlgorithm.SM2.CODE,pubKeyBytes[0]);
+ assertEquals(CryptoAlgorithm.SM2.CODE,privKeyBytes[0]);
+ assertEquals(CryptoAlgorithm.SM2, CryptoAlgorithm.valueOf(pubKey.getAlgorithm().CODE));
+ assertEquals(CryptoAlgorithm.SM2, CryptoAlgorithm.valueOf(privKey.getAlgorithm().CODE));
+
+ assertEquals(pubKey.getKeyType().CODE,pubKeyBytes[1]);
+ assertEquals(privKey.getKeyType().CODE,privKeyBytes[1]);
+
+
+ //test JNIED25519
+ algorithm = CryptoAlgorithm.JNIED25519;
+ keyPair = asymmetricCrypto.generateKeyPair(algorithm);
+
+ assertNotNull(keyPair);
+
+ pubKey = keyPair.getPubKey();
+ privKey = keyPair.getPrivKey();
+
+ assertNotNull(pubKey);
+ assertNotNull(privKey);
+
+ assertEquals(algorithm,pubKey.getAlgorithm());
+ assertEquals(algorithm,privKey.getAlgorithm());
+
+ assertEquals(32,pubKey.getRawKeyBytes().length);
+ assertEquals(32,privKey.getRawKeyBytes().length);
+
+ pubKeyBytes = pubKey.toBytes();
+ privKeyBytes = privKey.toBytes();
+
+ assertEquals(32+1+1,pubKeyBytes.length);
+ assertEquals(32+1+1,privKeyBytes.length);
+
+ assertEquals(CryptoAlgorithm.JNIED25519.CODE,pubKeyBytes[0]);
+ assertEquals(CryptoAlgorithm.JNIED25519.CODE,privKeyBytes[0]);
+ assertEquals(CryptoAlgorithm.JNIED25519, CryptoAlgorithm.valueOf(pubKey.getAlgorithm().CODE));
+ assertEquals(CryptoAlgorithm.JNIED25519, CryptoAlgorithm.valueOf(privKey.getAlgorithm().CODE));
+
+ assertEquals(pubKey.getKeyType().CODE,pubKeyBytes[1]);
+ assertEquals(privKey.getKeyType().CODE,privKeyBytes[1]);
+ }
+
+ @Test
+ public void testGetSignatureFunction() {
+
+ AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl();
+ Random random = new Random();
+
+
+ //test ED25519
+ CryptoAlgorithm algorithm = CryptoAlgorithm.ED25519;
+
+ // 测试256字节的消息进行签名
+ byte[] data = new byte[256];
+ random.nextBytes(data);
+ verifyGetSignatureFunction(asymmetricCrypto,algorithm,data,32,32,64,null);
+
+ //错误的算法标识
+ verifyGetSignatureFunction(asymmetricCrypto,CryptoAlgorithm.AES,data,32,32,64,IllegalArgumentException.class);
+
+ data = null;
+ verifyGetSignatureFunction(asymmetricCrypto,algorithm,data,32,32,64,NullPointerException.class);
+
+
+ //test SM2
+ algorithm = CryptoAlgorithm.SM2;
+
+ // 测试256字节的消息进行签名
+ data = new byte[256];
+ random.nextBytes(data);
+ verifyGetSignatureFunction(asymmetricCrypto,algorithm,data,65,32,64,null);
+
+ //错误的算法标识
+ verifyGetSignatureFunction(asymmetricCrypto,CryptoAlgorithm.AES,data,65,32,64,IllegalArgumentException.class);
+
+ data = null;
+ verifyGetSignatureFunction(asymmetricCrypto,algorithm,data,65,32,64,NullPointerException.class);
+
+
+ //test JNNIED25519
+ algorithm = CryptoAlgorithm.JNIED25519;
+
+ // 测试256字节的消息进行签名
+ data = new byte[256];
+ random.nextBytes(data);
+ verifyGetSignatureFunction(asymmetricCrypto,algorithm,data,32,32,64,null);
+
+ //错误的算法标识
+ verifyGetSignatureFunction(asymmetricCrypto,CryptoAlgorithm.AES,data,32,32,64,IllegalArgumentException.class);
+
+ data = null;
+ verifyGetSignatureFunction(asymmetricCrypto,algorithm,data,32,32,64,IllegalArgumentException.class);
+ }
+
+ private void verifyGetSignatureFunction(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm, byte[] data,
+ int expectedPubKeyLength, int expectedPrivKeyLength,
+ int expectedSignatureDigestLength, Class> expectedException){
+
+ //初始化一个异常
+ Exception actualEx = null;
+
+ try {
+ SignatureFunction sf = asymmetricCrypto.getSignatureFunction(algorithm);
+
+ assertNotNull(sf);
+
+ CryptoKeyPair keyPair = sf.generateKeyPair();
+ PubKey pubKey = keyPair.getPubKey();
+ PrivKey privKey = keyPair.getPrivKey();
+ byte[] rawPubKeyBytes = pubKey.getRawKeyBytes();
+ byte[] rawPrivKeyBytes = privKey.getRawKeyBytes();
+ byte[] pubKeyBytes = pubKey.toBytes();
+ byte[] privKeyBytes = privKey.toBytes();
+
+ assertEquals(algorithm, pubKey.getAlgorithm());
+ assertEquals(algorithm, privKey.getAlgorithm());
+ assertEquals(expectedPubKeyLength,rawPubKeyBytes.length);
+ assertEquals(expectedPrivKeyLength,rawPrivKeyBytes.length);
+
+ assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{CryptoKeyType.PUB_KEY.CODE},rawPubKeyBytes), pubKeyBytes);
+ assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{CryptoKeyType.PRIV_KEY.CODE},rawPrivKeyBytes), privKeyBytes);
+
+ SignatureDigest signatureDigest = sf.sign(privKey,data);
+ byte[] rawDigest = signatureDigest.getRawDigest();
+
+ assertEquals(algorithm,signatureDigest.getAlgorithm());
+ assertEquals(expectedSignatureDigestLength,rawDigest.length);
+ byte[] signatureDigestBytes = signatureDigest.toBytes();
+ assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},rawDigest),signatureDigestBytes);
+
+ assertTrue(signatureDigest.equals(signatureDigest));
+ assertEquals(signatureDigest.hashCode(),signatureDigest.hashCode());
+
+ assertTrue(sf.verify(signatureDigest,pubKey,data));
+
+ assertTrue(sf.supportPubKey(pubKeyBytes));
+ assertTrue(sf.supportPrivKey(privKeyBytes));
+ assertTrue(sf.supportDigest(signatureDigestBytes));
+
+ assertEquals(pubKey,sf.resolvePubKey(pubKeyBytes));
+ assertEquals(privKey,sf.resolvePrivKey(privKeyBytes));
+ assertEquals(signatureDigest,sf.resolveDigest(signatureDigestBytes));
+
+ assertEquals(algorithm,sf.getAlgorithm());
+
+ } catch (Exception e){
+ actualEx = e;
+ }
+
+ if (expectedException == null) {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testVerify() {
+
+ AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl();
+ Random randomData = new Random();
+
+ //test ED25519
+ CryptoAlgorithm algorithm = CryptoAlgorithm.ED25519;
+
+ // 测试256字节的消息进行签名
+ byte[] data = new byte[256];
+ randomData.nextBytes(data);
+ SignatureFunction sf = asymmetricCrypto.getSignatureFunction(algorithm);
+ CryptoKeyPair keyPair = sf.generateKeyPair();
+ byte[] pubKeyBytes = keyPair.getPubKey().toBytes();
+
+ byte[] signatureDigestBytes = sf.sign(keyPair.getPrivKey(),data).toBytes();
+ verifyVerify(asymmetricCrypto,true,data,pubKeyBytes,signatureDigestBytes,null);
+
+ //签名数据末尾两个字节丢失情况下,抛出异常
+ byte[] truncatedSignatureDigestBytes = new byte[signatureDigestBytes.length-2];
+ System.arraycopy(signatureDigestBytes,0,truncatedSignatureDigestBytes,0,truncatedSignatureDigestBytes.length);
+ verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,truncatedSignatureDigestBytes,IllegalArgumentException.class);
+
+ byte[] signatureDigestBytesWithWrongAlgCode = signatureDigestBytes;
+ signatureDigestBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,signatureDigestBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ signatureDigestBytes = null;
+ verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,signatureDigestBytes,NullPointerException.class);
+
+
+ //test SM2
+ algorithm = CryptoAlgorithm.SM2;
+
+ // 测试256字节的消息进行签名
+ data = new byte[256];
+ randomData.nextBytes(data);
+ sf = asymmetricCrypto.getSignatureFunction(algorithm);
+ keyPair = sf.generateKeyPair();
+ pubKeyBytes = keyPair.getPubKey().toBytes();
+
+ signatureDigestBytes = sf.sign(keyPair.getPrivKey(),data).toBytes();
+ verifyVerify(asymmetricCrypto,true,data,pubKeyBytes,signatureDigestBytes,null);
+
+ //签名数据末尾两个字节丢失情况下,抛出异常
+ truncatedSignatureDigestBytes = new byte[signatureDigestBytes.length-2];
+ System.arraycopy(signatureDigestBytes,0,truncatedSignatureDigestBytes,0,truncatedSignatureDigestBytes.length);
+ verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,truncatedSignatureDigestBytes,IllegalArgumentException.class);
+
+ signatureDigestBytesWithWrongAlgCode = signatureDigestBytes;
+ signatureDigestBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,signatureDigestBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ signatureDigestBytes = null;
+ verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,signatureDigestBytes,NullPointerException.class);
+
+ //test JNIED25519
+ algorithm = CryptoAlgorithm.JNIED25519;
+
+ // 测试256字节的消息进行签名
+ data = new byte[256];
+ randomData.nextBytes(data);
+ sf = asymmetricCrypto.getSignatureFunction(algorithm);
+ keyPair = sf.generateKeyPair();
+ pubKeyBytes = keyPair.getPubKey().toBytes();
+
+ signatureDigestBytes = sf.sign(keyPair.getPrivKey(),data).toBytes();
+ verifyVerify(asymmetricCrypto,true,data,pubKeyBytes,signatureDigestBytes,null);
+
+ //签名数据末尾两个字节丢失情况下,抛出异常
+ truncatedSignatureDigestBytes = new byte[signatureDigestBytes.length-2];
+ System.arraycopy(signatureDigestBytes,0,truncatedSignatureDigestBytes,0,truncatedSignatureDigestBytes.length);
+ verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,truncatedSignatureDigestBytes,IllegalArgumentException.class);
+
+ signatureDigestBytesWithWrongAlgCode = signatureDigestBytes;
+ signatureDigestBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,signatureDigestBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ signatureDigestBytes = null;
+ verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,signatureDigestBytes,NullPointerException.class);
+ }
+
+ private void verifyVerify(AsymmetricCryptography asymmetricCrypto,boolean expectedResult,byte[] data,
+ byte[] pubKeyBytes, byte[] signatureDigestBytes, Class> expectedException){
+
+ //初始化一个异常
+ Exception actualEx = null;
+ boolean pass = false;
+
+ try {
+
+ pass = asymmetricCrypto.verify(signatureDigestBytes,pubKeyBytes,data);
+
+ }
+ catch (Exception e){
+ actualEx = e;
+ }
+
+ assertEquals(expectedResult, pass);
+
+ if (expectedException == null) {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testGetAsymmetricEncryptionFunction() {
+
+ AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl();
+ Random random = new Random();
+
+
+ //test SM2
+ CryptoAlgorithm algorithm = CryptoAlgorithm.SM2;
+
+ //Case 1: SM2Encryption with 16 bytes data
+ byte[] data = new byte[16];
+ random.nextBytes(data);
+ verifyGetAsymmetricEncryptionFunction(asymmetricCrypto, algorithm,65,32,65+16+32,data,null);
+
+ //Case 2: SM2Encryption with 256 bytes data
+ data = new byte[256];
+ random.nextBytes(data);
+ verifyGetAsymmetricEncryptionFunction(asymmetricCrypto, algorithm,65,32,65+256+32,data,null);
+
+ //Case 3: SM2Encryption with 1 bytes data
+ data = new byte[3];
+ random.nextBytes(data);
+ verifyGetAsymmetricEncryptionFunction(asymmetricCrypto, algorithm,65,32,65+3+32,data,null);
+
+ //Case 4: SM2Encryption with wrong algorithm
+ verifyGetAsymmetricEncryptionFunction(asymmetricCrypto,CryptoAlgorithm.AES,65,32,65+3+32,data,IllegalArgumentException.class);
+
+ //Case 5: SM2Encryption with null data
+ data = null;
+ verifyGetAsymmetricEncryptionFunction(asymmetricCrypto,algorithm,65,32,65+32,data,NullPointerException.class);
+ }
+
+ private void verifyGetAsymmetricEncryptionFunction(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm,
+ int expectedPubKeyLength, int expectedPrivKeyLength,
+ int expectedCiphertextLength, byte[] data, Class> expectedException){
+
+ //初始化一个异常
+ Exception actualEx = null;
+
+ try {
+ AsymmetricEncryptionFunction aef = asymmetricCrypto.getAsymmetricEncryptionFunction(algorithm);
+ //验证获取的算法实例非空
+ assertNotNull(aef);
+
+ CryptoKeyPair keyPair = aef.generateKeyPair();
+ PubKey pubKey = keyPair.getPubKey();
+ PrivKey privKey = keyPair.getPrivKey();
+ byte[] rawPubKeyBytes = pubKey.getRawKeyBytes();
+ byte[] rawPrivKeyBytes = privKey.getRawKeyBytes();
+ byte[] pubKeyBytes = pubKey.toBytes();
+ byte[] privKeyBytes = privKey.toBytes();
+
+ assertEquals(algorithm, pubKey.getAlgorithm());
+ assertEquals(algorithm, privKey.getAlgorithm());
+ assertEquals(expectedPubKeyLength,rawPubKeyBytes.length);
+ assertEquals(expectedPrivKeyLength,rawPrivKeyBytes.length);
+
+ assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{CryptoKeyType.PUB_KEY.CODE},rawPubKeyBytes), pubKeyBytes);
+ assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{CryptoKeyType.PRIV_KEY.CODE},rawPrivKeyBytes), privKeyBytes);
+
+ Ciphertext ciphertext = aef.encrypt(pubKey,data);
+ byte[] rawCiphertextBytes = ciphertext.getRawCiphertext();
+
+ assertEquals(algorithm,ciphertext.getAlgorithm());
+ assertEquals(expectedCiphertextLength,rawCiphertextBytes.length);
+ byte[] ciphertextBytes = ciphertext.toBytes();
+ assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},rawCiphertextBytes),ciphertextBytes);
+
+ assertArrayEquals(data,aef.decrypt(privKey,ciphertext));
+
+ assertTrue(aef.supportPubKey(pubKeyBytes));
+ assertTrue(aef.supportPrivKey(privKeyBytes));
+ assertTrue(aef.supportCiphertext(ciphertextBytes));
+
+ assertEquals(pubKey,aef.resolvePubKey(pubKeyBytes));
+ assertEquals(privKey,aef.resolvePrivKey(privKeyBytes));
+ assertEquals(ciphertext,aef.resolveCiphertext(ciphertextBytes));
+
+ assertEquals(algorithm,aef.getAlgorithm());
+
+
+ }catch (Exception e){
+ actualEx = e;
+ }
+
+ if(expectedException == null){
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testDecrypt() {
+
+ AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl();
+ Random random = new Random();
+
+ byte[] data = new byte[16];
+ random.nextBytes(data);
+
+ //test SM2
+ CryptoAlgorithm algorithm = CryptoAlgorithm.SM2;
+ AsymmetricEncryptionFunction aef = asymmetricCrypto.getAsymmetricEncryptionFunction(algorithm);
+ CryptoKeyPair keyPair = aef.generateKeyPair();
+ PubKey pubKey = keyPair.getPubKey();
+ PrivKey privKey = keyPair.getPrivKey();
+ byte[] rawPrivKeyBytes = privKey.getRawKeyBytes();
+ Ciphertext ciphertext = aef.encrypt(pubKey,data);
+ byte[] ciphertextBytes = ciphertext.toBytes();
+
+ verifyDecrypt(asymmetricCrypto, algorithm, rawPrivKeyBytes, data, ciphertextBytes, null);
+
+ //密钥的算法标识与密文的算法标识不一致情况
+ verifyDecrypt(asymmetricCrypto, CryptoAlgorithm.AES, rawPrivKeyBytes, data, ciphertextBytes, IllegalArgumentException.class);
+
+ //密文末尾两个字节丢失情况下,抛出异常
+ byte[] truncatedCiphertextBytes = new byte[ciphertextBytes.length-2];
+ System.arraycopy(ciphertextBytes,0,truncatedCiphertextBytes,0,truncatedCiphertextBytes.length);
+ verifyDecrypt(asymmetricCrypto, algorithm, rawPrivKeyBytes, data, truncatedCiphertextBytes, com.jd.blockchain.crypto.CryptoException.class);
+
+ byte[] ciphertextBytesWithWrongAlgCode = ciphertextBytes;
+ ciphertextBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyDecrypt(asymmetricCrypto,algorithm,rawPrivKeyBytes,data,ciphertextBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ ciphertextBytes = null;
+ verifyDecrypt(asymmetricCrypto,algorithm,rawPrivKeyBytes,data,ciphertextBytes,NullPointerException.class);
+ }
+
+ private void verifyDecrypt(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm,
+ byte[] key, byte[] data, byte[] ciphertextBytes, Class> expectedException){
+ Exception actualEx = null;
+
+ try {
+ PrivKey privKey = new PrivKey(algorithm,key);
+
+ byte[] plaintext = asymmetricCrypto.decrypt(privKey.toBytes(), ciphertextBytes);
+
+ //解密后的明文与初始的明文一致
+ assertArrayEquals(data,plaintext);
+ }
+ catch (Exception e){
+ actualEx = e;
+ }
+
+ if (expectedException == null) {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testResolveCiphertext() {
+
+
+ AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl();
+ Random random = new Random();
+
+ byte[] data = new byte[16];
+ random.nextBytes(data);
+
+ //test SM2
+ CryptoAlgorithm algorithm = CryptoAlgorithm.SM2;
+ AsymmetricEncryptionFunction aef = asymmetricCrypto.getAsymmetricEncryptionFunction(algorithm);
+ CryptoKeyPair keyPair = aef.generateKeyPair();
+ PubKey pubKey = keyPair.getPubKey();
+ PrivKey privKey = keyPair.getPrivKey();
+ byte[] rawPrivKeyBytes = privKey.getRawKeyBytes();
+ Ciphertext ciphertext = aef.encrypt(pubKey,data);
+ byte[] ciphertextBytes = ciphertext.toBytes();
+
+ verifyResolveCiphertext(asymmetricCrypto, algorithm, ciphertextBytes, null);
+
+
+ //密文末尾两个字节丢失情况下,抛出异常
+ byte[] truncatedCiphertextBytes = new byte[ciphertextBytes.length-2];
+ System.arraycopy(ciphertextBytes,0,truncatedCiphertextBytes,0,truncatedCiphertextBytes.length);
+ verifyDecrypt(asymmetricCrypto, algorithm, rawPrivKeyBytes, data, truncatedCiphertextBytes, CryptoException.class);
+
+ byte[] ciphertextBytesWithWrongAlgCode = ciphertextBytes;
+ ciphertextBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyResolveCiphertext(asymmetricCrypto,algorithm,ciphertextBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ ciphertextBytes = null;
+ verifyResolveCiphertext(asymmetricCrypto,algorithm,ciphertextBytes,NullPointerException.class);
+ }
+
+ private void verifyResolveCiphertext(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm, byte[] ciphertextBytes,
+ Class> expectedException){
+ Exception actualEx = null;
+
+ try {
+
+ Ciphertext ciphertext = asymmetricCrypto.resolveCiphertext(ciphertextBytes);
+
+ assertNotNull(ciphertext);
+
+ assertEquals(algorithm, ciphertext.getAlgorithm());
+
+ assertArrayEquals(ciphertextBytes, ciphertext.toBytes());
+ }
+ catch (Exception e){
+ actualEx = e;
+ }
+
+ if (expectedException == null) {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testTryResolveCiphertext() {
+ }
+
+ @Test
+ public void testResolveSignatureDigest() {
+
+ AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl();
+ Random randomData = new Random();
+
+ //test ED25519
+ CryptoAlgorithm algorithm = CryptoAlgorithm.ED25519;
+
+ // 测试256字节的消息进行签名
+ byte[] data = new byte[256];
+ randomData.nextBytes(data);
+ SignatureFunction sf = asymmetricCrypto.getSignatureFunction(algorithm);
+ CryptoKeyPair keyPair = sf.generateKeyPair();
+
+ byte[] signatureDigestBytes = sf.sign(keyPair.getPrivKey(),data).toBytes();
+ verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,signatureDigestBytes,null);
+
+ //签名数据末尾两个字节丢失情况下,抛出异常
+ byte[] truncatedSignatureDigestBytes = new byte[signatureDigestBytes.length-2];
+ System.arraycopy(signatureDigestBytes,0,truncatedSignatureDigestBytes,0,truncatedSignatureDigestBytes.length);
+ verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,truncatedSignatureDigestBytes,IllegalArgumentException.class);
+
+ signatureDigestBytes = null;
+ verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,signatureDigestBytes,NullPointerException.class);
+
+
+ //test SM2
+ algorithm = CryptoAlgorithm.SM2;
+
+ // 测试256字节的消息进行签名
+ data = new byte[256];
+ randomData.nextBytes(data);
+ sf = asymmetricCrypto.getSignatureFunction(algorithm);
+ keyPair = sf.generateKeyPair();
+
+ signatureDigestBytes = sf.sign(keyPair.getPrivKey(),data).toBytes();
+ verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,signatureDigestBytes,null);
+
+ //签名数据末尾两个字节丢失情况下,抛出异常
+ truncatedSignatureDigestBytes = new byte[signatureDigestBytes.length-2];
+ System.arraycopy(signatureDigestBytes,0,truncatedSignatureDigestBytes,0,truncatedSignatureDigestBytes.length);
+ verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,truncatedSignatureDigestBytes,IllegalArgumentException.class);
+
+ signatureDigestBytes = null;
+ verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,signatureDigestBytes,NullPointerException.class);
+
+ //test JNIED25519
+ algorithm = CryptoAlgorithm.JNIED25519;
+
+ // 测试256字节的消息进行签名
+ data = new byte[256];
+ randomData.nextBytes(data);
+ sf = asymmetricCrypto.getSignatureFunction(algorithm);
+ keyPair = sf.generateKeyPair();
+
+ signatureDigestBytes = sf.sign(keyPair.getPrivKey(),data).toBytes();
+ verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,signatureDigestBytes,null);
+
+ //签名数据末尾两个字节丢失情况下,抛出异常
+ truncatedSignatureDigestBytes = new byte[signatureDigestBytes.length-2];
+ System.arraycopy(signatureDigestBytes,0,truncatedSignatureDigestBytes,0,truncatedSignatureDigestBytes.length);
+ verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,truncatedSignatureDigestBytes,IllegalArgumentException.class);
+
+ signatureDigestBytes = null;
+ verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,signatureDigestBytes,NullPointerException.class);
+ }
+
+ private void verifyResolveSignatureDigest(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm,
+ int expectedSignatureDigestLength,
+ byte[] signatureDigestBytes, Class> expectedException){
+
+ //初始化一个异常
+ Exception actualEx = null;
+
+ try {
+
+ SignatureDigest signatureDigest = asymmetricCrypto.resolveSignatureDigest(signatureDigestBytes);
+
+ assertNotNull(signatureDigest);
+
+ assertEquals(algorithm,signatureDigest.getAlgorithm());
+
+ assertEquals(expectedSignatureDigestLength,signatureDigest.getRawDigest().length);
+
+ assertArrayEquals(signatureDigestBytes,signatureDigest.toBytes());
+
+ }
+ catch (Exception e){
+ actualEx = e;
+ }
+
+ if (expectedException == null) {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testTryResolveSignatureDigest() {
+ }
+
+ @Test
+ public void testResolvePubKey() {
+
+ AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl();
+
+ //test ED25519
+ CryptoAlgorithm algorithm = CryptoAlgorithm.ED25519;
+
+ CryptoKeyPair keyPair = asymmetricCrypto.generateKeyPair(algorithm);
+
+ byte[] pubKeyBytes = keyPair.getPubKey().toBytes();
+ verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytes,null);
+
+ byte[] truncatedPubKeyBytes = new byte[pubKeyBytes.length-2];
+ System.arraycopy(pubKeyBytes,0,truncatedPubKeyBytes,0,truncatedPubKeyBytes.length);
+ verifyResolvePubKey(asymmetricCrypto,algorithm,32,truncatedPubKeyBytes,IllegalArgumentException.class);
+
+ byte[] pubKeyBytesWithWrongAlgCode = pubKeyBytes;
+ pubKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ byte[] pubKeyBytesWithWrongKeyType= pubKeyBytes;
+ pubKeyBytesWithWrongKeyType[1] = PRIV_KEY.CODE;
+ verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytesWithWrongKeyType,IllegalArgumentException.class);
+
+ pubKeyBytes = null;
+ verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytes,NullPointerException.class);
+
+
+ //test SM2
+ algorithm = CryptoAlgorithm.SM2;
+
+ keyPair = asymmetricCrypto.generateKeyPair(algorithm);
+
+ pubKeyBytes = keyPair.getPubKey().toBytes();
+ verifyResolvePubKey(asymmetricCrypto,algorithm,65,pubKeyBytes,null);
+
+ truncatedPubKeyBytes = new byte[pubKeyBytes.length-2];
+ System.arraycopy(pubKeyBytes,0,truncatedPubKeyBytes,0,truncatedPubKeyBytes.length);
+ verifyResolvePubKey(asymmetricCrypto,algorithm,65,truncatedPubKeyBytes,IllegalArgumentException.class);
+
+ pubKeyBytesWithWrongAlgCode = pubKeyBytes;
+ pubKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyResolvePubKey(asymmetricCrypto,algorithm,65,pubKeyBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ pubKeyBytesWithWrongKeyType= pubKeyBytes;
+ pubKeyBytesWithWrongKeyType[1] = PRIV_KEY.CODE;
+ verifyResolvePubKey(asymmetricCrypto,algorithm,65,pubKeyBytesWithWrongKeyType,IllegalArgumentException.class);
+
+ pubKeyBytes = null;
+ verifyResolvePubKey(asymmetricCrypto,algorithm,65,pubKeyBytes,NullPointerException.class);
+
+ //test JNIED25519
+ algorithm = CryptoAlgorithm.JNIED25519;
+
+ keyPair = asymmetricCrypto.generateKeyPair(algorithm);
+
+ pubKeyBytes = keyPair.getPubKey().toBytes();
+ verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytes,null);
+
+ truncatedPubKeyBytes = new byte[pubKeyBytes.length-2];
+ System.arraycopy(pubKeyBytes,0,truncatedPubKeyBytes,0,truncatedPubKeyBytes.length);
+ verifyResolvePubKey(asymmetricCrypto,algorithm,32,truncatedPubKeyBytes,IllegalArgumentException.class);
+
+ pubKeyBytesWithWrongAlgCode = pubKeyBytes;
+ pubKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ pubKeyBytesWithWrongKeyType= pubKeyBytes;
+ pubKeyBytesWithWrongKeyType[1] = PRIV_KEY.CODE;
+ verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytesWithWrongKeyType,IllegalArgumentException.class);
+
+ pubKeyBytes = null;
+ verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytes,NullPointerException.class);
+ }
+
+ private void verifyResolvePubKey(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm,
+ int expectedPubKeyLength, byte[] pubKeyBytes,Class> expectedException){
+
+ Exception actualEx = null;
+
+ try {
+ PubKey pubKey = asymmetricCrypto.resolvePubKey(pubKeyBytes);
+
+ assertNotNull(pubKey);
+
+ assertEquals(algorithm, pubKey.getAlgorithm());
+
+ assertEquals(expectedPubKeyLength, pubKey.getRawKeyBytes().length);
+
+ assertArrayEquals(pubKeyBytes, pubKey.toBytes());
+
+ }
+ catch (Exception e){
+ actualEx = e;
+ }
+
+ if (expectedException == null) {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testTryResolvePubKey() {
+ }
+
+ @Test
+ public void testResolvePrivKey() {
+
+ AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl();
+
+ //test ED25519
+ CryptoAlgorithm algorithm = CryptoAlgorithm.ED25519;
+
+ CryptoKeyPair keyPair = asymmetricCrypto.generateKeyPair(algorithm);
+
+ byte[] privKeyBytes = keyPair.getPrivKey().toBytes();
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytes,null);
+
+ byte[] truncatedPrivKeyBytes = new byte[privKeyBytes.length-2];
+ System.arraycopy(privKeyBytes,0,truncatedPrivKeyBytes,0,truncatedPrivKeyBytes.length);
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,truncatedPrivKeyBytes,IllegalArgumentException.class);
+
+ byte[] privKeyBytesWithWrongAlgCode = privKeyBytes;
+ privKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ byte[] privKeyBytesWithWrongKeyType = privKeyBytes;
+ privKeyBytesWithWrongKeyType[1] = PUB_KEY.CODE;
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytesWithWrongKeyType,IllegalArgumentException.class);
+
+ privKeyBytes = null;
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytes,NullPointerException.class);
+
+
+ //test SM2
+ algorithm = CryptoAlgorithm.SM2;
+
+ keyPair = asymmetricCrypto.generateKeyPair(algorithm);
+
+ privKeyBytes = keyPair.getPrivKey().toBytes();
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytes,null);
+
+ truncatedPrivKeyBytes = new byte[privKeyBytes.length-2];
+ System.arraycopy(privKeyBytes,0,truncatedPrivKeyBytes,0,truncatedPrivKeyBytes.length);
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,truncatedPrivKeyBytes,IllegalArgumentException.class);
+
+ privKeyBytesWithWrongAlgCode = privKeyBytes;
+ privKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ privKeyBytesWithWrongKeyType = privKeyBytes;
+ privKeyBytesWithWrongKeyType[1] = PUB_KEY.CODE;
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytesWithWrongKeyType,IllegalArgumentException.class);
+
+ privKeyBytes = null;
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytes,NullPointerException.class);
+
+ //test JNIED25519
+ algorithm = CryptoAlgorithm.JNIED25519;
+
+ keyPair = asymmetricCrypto.generateKeyPair(algorithm);
+
+ privKeyBytes = keyPair.getPrivKey().toBytes();
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytes,null);
+
+ truncatedPrivKeyBytes = new byte[privKeyBytes.length-2];
+ System.arraycopy(privKeyBytes,0,truncatedPrivKeyBytes,0,truncatedPrivKeyBytes.length);
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,truncatedPrivKeyBytes,IllegalArgumentException.class);
+
+ privKeyBytesWithWrongAlgCode = privKeyBytes;
+ privKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ privKeyBytesWithWrongKeyType = privKeyBytes;
+ privKeyBytesWithWrongKeyType[1] = PUB_KEY.CODE;
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytesWithWrongKeyType,IllegalArgumentException.class);
+
+ privKeyBytes = null;
+ verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytes,NullPointerException.class);
+ }
+
+ private void verifyResolvePrivKey(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm,
+ int expectedPrivKeyLength, byte[] privKeyBytes,Class> expectedException){
+
+ Exception actualEx = null;
+
+ try {
+ PrivKey privKey = asymmetricCrypto.resolvePrivKey(privKeyBytes);
+
+ assertNotNull(privKey);
+
+ assertEquals(algorithm, privKey.getAlgorithm());
+
+ assertEquals(expectedPrivKeyLength, privKey.getRawKeyBytes().length);
+
+ assertArrayEquals(privKeyBytes, privKey.toBytes());
+
+ }
+ catch (Exception e){
+ actualEx = e;
+ }
+
+ if (expectedException == null) {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testTryResolvePrivKey() {
+ }
+}
\ No newline at end of file
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/hash/HashCryptographyImplTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/hash/HashCryptographyImplTest.java
new file mode 100644
index 00000000..513cb577
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/hash/HashCryptographyImplTest.java
@@ -0,0 +1,333 @@
+package test.com.jd.blockchain.crypto.hash;
+
+import static org.junit.Assert.*;
+
+import java.util.Random;
+
+import com.jd.blockchain.crypto.smutils.hash.SM3Utils;
+import com.jd.blockchain.utils.io.BytesUtils;
+import com.jd.blockchain.utils.security.RipeMD160Utils;
+import com.jd.blockchain.utils.security.ShaUtils;
+
+import org.junit.Test;
+
+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.HashCryptographyImpl;
+
+public class HashCryptographyImplTest {
+
+ @Test
+ public void testGetFunction() {
+ HashCryptography hashCrypto = new HashCryptographyImpl();
+ Random rand = new Random();
+ // test SHA256
+ CryptoAlgorithm algorithm = CryptoAlgorithm.SHA256;
+ byte[] data = new byte[256];
+ rand.nextBytes(data);
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null);
+
+ data = new byte[0];
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null);
+
+ data = new byte[1056];
+ rand.nextBytes(data);
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null);
+
+ data = null;
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,NullPointerException.class);
+
+
+ // test RIPEMD160
+ algorithm = CryptoAlgorithm.RIPEMD160;
+ data=new byte[256];
+ rand.nextBytes(data);
+ verifyGetFunction(hashCrypto, algorithm, data, 160 / 8,null);
+
+ data = new byte[0];
+ verifyGetFunction(hashCrypto, algorithm, data, 160/ 8,null);
+
+ data = new byte[1056];
+ rand.nextBytes(data);
+ verifyGetFunction(hashCrypto, algorithm, data, 160 / 8,null);
+
+ data = null;
+ verifyGetFunction(hashCrypto, algorithm, data, 160 / 8,NullPointerException.class);
+
+ // test SM3
+ algorithm = CryptoAlgorithm.SM3;
+ data = new byte[256];
+ rand.nextBytes(data);
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null);
+
+ data = new byte[0];
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null);
+
+ data = new byte[1056];
+ rand.nextBytes(data);
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null);
+
+ data = null;
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,NullPointerException.class);
+
+ // test AES
+ data = new byte[0];
+ algorithm = CryptoAlgorithm.AES;
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,IllegalArgumentException.class);
+
+ // test JNISHA256
+ algorithm = CryptoAlgorithm.JNISHA256;
+ data = new byte[256];
+ rand.nextBytes(data);
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null);
+
+ data = new byte[0];
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null);
+
+ data = new byte[1056];
+ rand.nextBytes(data);
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null);
+
+ data = null;
+ verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,IllegalArgumentException.class);
+
+ // test JNIRIPEMD160
+ algorithm = CryptoAlgorithm.JNIRIPEMD160;
+ data=new byte[256];
+ rand.nextBytes(data);
+ verifyGetFunction(hashCrypto, algorithm, data, 160 / 8,null);
+
+ data = new byte[0];
+ verifyGetFunction(hashCrypto, algorithm, data, 160/ 8,null);
+
+ data = new byte[1056];
+ rand.nextBytes(data);
+ verifyGetFunction(hashCrypto, algorithm, data, 160 / 8,null);
+
+ data = null;
+ verifyGetFunction(hashCrypto, algorithm, data, 160 / 8,IllegalArgumentException.class);
+ }
+
+ private void verifyGetFunction(HashCryptography hashCrypto, CryptoAlgorithm algorithm, byte[] data,
+ int expectedRawBytes,Class> expectedException) {
+ Exception actualEx = null;
+ try {
+ HashFunction hf = hashCrypto.getFunction(algorithm);
+ assertNotNull(hf);
+
+ HashDigest hd = hf.hash(data);
+
+ assertEquals(algorithm, hd.getAlgorithm());
+
+ assertEquals(expectedRawBytes, hd.getRawDigest().length);
+
+ // verify encoding;
+ byte[] encodedHash = hd.toBytes();
+ assertEquals(expectedRawBytes + 1, encodedHash.length);
+
+
+ assertEquals(algorithm.CODE, encodedHash[0]);
+
+ //verify equals
+ assertEquals(true, hd.equals(hf.hash(data)));
+
+ //verify verify
+ assertTrue( hf.verify(hd, data));
+
+ } catch (Exception e) {
+ actualEx = e;
+ }
+
+ if(expectedException==null){
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testVerifyHashDigestByteArray() {
+ HashCryptography hashCrypto = new HashCryptographyImpl();
+ //test SHA256
+ byte[] data=new byte[256];
+ Random rand = new Random();
+ rand.nextBytes(data);
+ CryptoAlgorithm algorithm=CryptoAlgorithm.SHA256;
+ verifyHashDigestByteArray(hashCrypto,algorithm,data,null);
+ data=null;
+ verifyHashDigestByteArray(hashCrypto,algorithm,data,NullPointerException.class);
+
+ //test RIPEMD160
+ algorithm=CryptoAlgorithm.RIPEMD160;
+ data=new byte[896];
+ rand.nextBytes(data);
+ verifyHashDigestByteArray(hashCrypto,algorithm,data,null);
+ data=null;
+ verifyHashDigestByteArray(hashCrypto,algorithm,data,NullPointerException.class);
+
+ //test SM3
+ algorithm=CryptoAlgorithm.SM3;
+ data=new byte[896];
+ rand.nextBytes(data);
+ verifyHashDigestByteArray(hashCrypto,algorithm,data,null);
+ data=null;
+ verifyHashDigestByteArray(hashCrypto,algorithm,data,NullPointerException.class);
+
+
+ //test AES
+ algorithm=CryptoAlgorithm.AES;
+ data=new byte[277];
+ rand.nextBytes(data);
+ verifyHashDigestByteArray(hashCrypto,algorithm,data,IllegalArgumentException.class);
+
+ //test JNISHA256
+ data=new byte[256];
+ rand = new Random();
+ rand.nextBytes(data);
+ algorithm=CryptoAlgorithm.JNISHA256;
+ verifyHashDigestByteArray(hashCrypto,algorithm,data,null);
+ data=null;
+ verifyHashDigestByteArray(hashCrypto,algorithm,data,IllegalArgumentException.class);
+
+ //test JNIRIPEMD160
+ algorithm=CryptoAlgorithm.JNIRIPEMD160;
+ data=new byte[896];
+ rand.nextBytes(data);
+ verifyHashDigestByteArray(hashCrypto,algorithm,data,null);
+ data=null;
+ verifyHashDigestByteArray(hashCrypto,algorithm,data,IllegalArgumentException.class);
+ }
+
+ private void verifyHashDigestByteArray(HashCryptography hashCrypto,CryptoAlgorithm algorithm,byte[] data,Class> expectedException){
+ Exception actualEx=null;
+ try {
+ HashFunction hf = hashCrypto.getFunction(algorithm);
+ assertNotNull(hf);
+ HashDigest hd = hf.hash(data);
+ hashCrypto.verify(hd,data);
+ }catch (Exception e)
+ {
+ actualEx=e;
+ }
+ if (expectedException==null)
+ {
+ assertNull(actualEx);
+ }
+ else{
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testResolveHashDigest() {
+ Random rand = new Random();
+ HashCryptography hashCrypto = new HashCryptographyImpl();
+
+ //test SHA256
+ CryptoAlgorithm algorithm = CryptoAlgorithm.SHA256;
+ byte[] data = new byte[256];
+ rand.nextBytes(data);
+ byte[] hashDigestBytes = hashCrypto.getFunction(algorithm).hash(data).toBytes();
+ verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,32+1,null);
+
+ byte[] truncatedHashDigestBytes = new byte[hashDigestBytes.length-2];
+ System.arraycopy(hashDigestBytes,0,truncatedHashDigestBytes,0,truncatedHashDigestBytes.length);
+ verifyResolveHashDigest(algorithm, hashCrypto,truncatedHashDigestBytes,32+1,IllegalArgumentException.class);
+
+ hashDigestBytes = null;
+ verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,32+1,NullPointerException.class);
+
+
+ //test RIPEMD160
+ algorithm = CryptoAlgorithm.RIPEMD160;
+ data = new byte[256];
+ rand.nextBytes(data);
+ hashDigestBytes = hashCrypto.getFunction(algorithm).hash(data).toBytes();
+ verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,20+1,null);
+
+ truncatedHashDigestBytes = new byte[hashDigestBytes.length-2];
+ System.arraycopy(hashDigestBytes,0,truncatedHashDigestBytes,0,truncatedHashDigestBytes.length);
+ verifyResolveHashDigest(algorithm, hashCrypto,truncatedHashDigestBytes,20+1,IllegalArgumentException.class);
+
+ hashDigestBytes = null;
+ verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,20+1,NullPointerException.class);
+
+
+ //test SM3
+ algorithm = CryptoAlgorithm.SM3;
+ data = new byte[256];
+ rand.nextBytes(data);
+ hashDigestBytes = hashCrypto.getFunction(algorithm).hash(data).toBytes();
+ verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,32+1,null);
+
+ truncatedHashDigestBytes = new byte[hashDigestBytes.length-2];
+ System.arraycopy(hashDigestBytes,0,truncatedHashDigestBytes,0,truncatedHashDigestBytes.length);
+ verifyResolveHashDigest(algorithm, hashCrypto,truncatedHashDigestBytes,32+1,IllegalArgumentException.class);
+
+ hashDigestBytes = null;
+ verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,32+1,NullPointerException.class);
+
+
+ //test JNISHA256
+ algorithm = CryptoAlgorithm.JNISHA256;
+ data = new byte[256];
+ rand.nextBytes(data);
+ hashDigestBytes = hashCrypto.getFunction(algorithm).hash(data).toBytes();
+ verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,32+1,null);
+
+ truncatedHashDigestBytes = new byte[hashDigestBytes.length-2];
+ System.arraycopy(hashDigestBytes,0,truncatedHashDigestBytes,0,truncatedHashDigestBytes.length);
+ verifyResolveHashDigest(algorithm, hashCrypto,truncatedHashDigestBytes,32+1,IllegalArgumentException.class);
+
+ hashDigestBytes = null;
+ verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,32+1,NullPointerException.class);
+
+ //test JNIRIPEMD160
+ algorithm = CryptoAlgorithm.JNIRIPEMD160;
+ data = new byte[256];
+ rand.nextBytes(data);
+ hashDigestBytes = hashCrypto.getFunction(algorithm).hash(data).toBytes();
+ verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,20+1,null);
+
+ truncatedHashDigestBytes = new byte[hashDigestBytes.length-2];
+ System.arraycopy(hashDigestBytes,0,truncatedHashDigestBytes,0,truncatedHashDigestBytes.length);
+ verifyResolveHashDigest(algorithm, hashCrypto,truncatedHashDigestBytes,20+1,IllegalArgumentException.class);
+
+ hashDigestBytes = null;
+ verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,20+1,NullPointerException.class);
+ }
+
+ private void verifyResolveHashDigest(CryptoAlgorithm algorithm,HashCryptography
+ hashCrypto,byte[] hashDigestBytes,int expectedLength,Class>expectedException){
+
+ Exception actualEx=null;
+
+ try {
+
+ HashDigest hashDigest=hashCrypto.resolveHashDigest(hashDigestBytes);
+ assertNotNull(hashDigest);
+ assertEquals(algorithm,hashDigest.getAlgorithm());
+ byte[] algBytes = new byte[1];
+ algBytes[0] = algorithm.CODE;
+ assertArrayEquals(hashDigestBytes,BytesUtils.concat(algBytes,hashDigest.getRawDigest()));
+ assertEquals(expectedLength,hashDigestBytes.length);
+
+ }catch (Exception e)
+ {
+ actualEx = e;
+ }
+ if (expectedException==null)
+ {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+ }
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIED25519UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIED25519UtilsTest.java
new file mode 100644
index 00000000..8cbce2bb
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIED25519UtilsTest.java
@@ -0,0 +1,123 @@
+package test.com.jd.blockchain.crypto.jniutils;
+
+import com.jd.blockchain.crypto.jniutils.asymmetric.JNIED25519Utils;
+
+
+public class JNIED25519UtilsTest {
+
+ /* Program entry function */
+ public static void main(String args[]) {
+
+ byte[] msg = "abc".getBytes();
+ int i;
+ int j;
+ int count = 10000;
+
+ long startTS;
+ long elapsedTS;
+
+ byte[] privKey = new byte[32];
+ byte[] pubKey = new byte[32];
+ byte[] signature;
+
+
+ JNIED25519Utils ed25519 = new JNIED25519Utils();
+
+ System.out.println("=================== Key Generation test ===================");
+ ed25519.generateKeyPair(privKey,pubKey);
+ System.out.println("Private Key: ");
+ for(i = 0; i < privKey.length; i++) {
+ System.out.print(privKey[i] + " ");
+ if((i+1)%8 == 0)
+ System.out.println();
+ }
+ System.out.println();
+ System.out.println("Public Key: ");
+ for(i = 0; i < pubKey.length; i++) {
+ System.out.print(pubKey[i] + " ");
+ if((i+1)%8 == 0)
+ System.out.println();
+ }
+ System.out.println();
+
+ System.out.println("=================== Public Key Retrieval test ===================");
+ byte[] pk;
+ pk = ed25519.getPubKey(privKey);
+ System.out.println("Retrieved Public Key: ");
+ for(i = 0; i < pk.length; i++) {
+ System.out.print(pk[i] + " ");
+ if((i+1)%8 == 0)
+ System.out.println();
+ }
+ System.out.println();
+
+ System.out.println("=================== Signing test ===================");
+ signature = ed25519.sign(msg,privKey,pubKey);
+ System.out.println("Signature: ");
+ for(i = 0; i < signature.length; i++) {
+ System.out.print(signature[i] + " ");
+ if((i+1)%8 == 0)
+ System.out.println();
+ }
+ System.out.println();
+
+ System.out.println("=================== Verifying test ===================");
+ if (ed25519.verify(msg,pubKey,signature))
+ System.out.println("valid signature");
+ else System.out.println("invalid signature");
+
+ System.out.println("=================== Do ED25519 Key Pair Generation Test ===================");
+
+
+ for (j = 0; j < 5; j++) {
+ System.out.println("------------- round[" + j + "] --------------");
+ startTS = System.currentTimeMillis();
+ for (i = 0; i < count; i++) {
+ ed25519.generateKeyPair(privKey,pubKey);
+ }
+ elapsedTS = System.currentTimeMillis() - startTS;
+ System.out.println(String.format("ED25519 Key Pair Generation: Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS,
+ (count * 1000.00D) / elapsedTS));
+ }
+ System.out.println();
+
+ System.out.println("=================== Do ED25519 Public Key Retrieval Test ===================");
+ for (j = 0; j < 5; j++) {
+ System.out.println("------------- round[" + j + "] --------------");
+ startTS = System.currentTimeMillis();
+ for (i = 0; i < count; i++) {
+ ed25519.getPubKey(privKey);
+ }
+ elapsedTS = System.currentTimeMillis() - startTS;
+ System.out.println(String.format("ED25519 Public Key Retrieval: Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS,
+ (count * 1000.00D) / elapsedTS));
+ }
+ System.out.println();
+
+ System.out.println("=================== Do ED25519 Signing Test ===================");
+ for (j = 0; j < 5; j++) {
+ System.out.println("------------- round[" + j + "] --------------");
+ startTS = System.currentTimeMillis();
+ for (i = 0; i < count; i++) {
+ ed25519.sign(msg,privKey,pubKey);
+ }
+ 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();
+
+ System.out.println("=================== Do ED25519 Verifying Test ===================");
+ for (j = 0; j < 5; j++) {
+ System.out.println("------------- round[" + j + "] --------------");
+ startTS = System.currentTimeMillis();
+ for (i = 0; i < count; i++) {
+ ed25519.verify(msg,pubKey,signature);
+ }
+ elapsedTS = System.currentTimeMillis() - startTS;
+ System.out.println(String.format("ED25519 Verifying: Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS,
+ (count * 1000.00D) / elapsedTS));
+ }
+ System.out.println();
+ }
+}
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIMBSHA256UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIMBSHA256UtilsTest.java
new file mode 100644
index 00000000..e5b5ef2f
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIMBSHA256UtilsTest.java
@@ -0,0 +1,111 @@
+package test.com.jd.blockchain.crypto.jniutils;
+
+import com.jd.blockchain.crypto.jniutils.hash.JNIMBSHA256Utils;
+
+public class JNIMBSHA256UtilsTest {
+ /* Program entry function */
+ public static void main(String args[]) {
+
+ String osName = System.getProperty("os.name").toLowerCase();
+
+ if (! osName.contains("linux")) {
+ return ;
+ }
+
+ byte[] array1 = "abc".getBytes();
+ byte[] array2 = "abcd".getBytes();
+ byte[] array3 = "abcde".getBytes();
+ byte[] array4 = "abcdef".getBytes();
+
+ byte[][] arrays = {array1,array2,array3,array4};
+ JNIMBSHA256Utils mbsha256 = new JNIMBSHA256Utils();
+ byte[][] results = mbsha256.multiBufferHash(arrays);
+
+ System.out.println("JAVA to C : ");
+ for (int i = 0; i < arrays.length; i++) {
+ for (int j = 0; j < arrays[i].length; j++) {
+ System.out.print(arrays[i][j] + " ");
+ }
+ System.out.println();
+ }
+
+ System.out.println();
+
+ System.out.println("C to JAVA : ");
+ for (int i = 0; i < results.length; i++) {
+ for (int j = 0; j < results[i].length; j++) {
+ System.out.print(results[i][j] + " ");
+ }
+ System.out.println();
+ }
+
+ System.out.println();
+
+ String str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
+ byte[] array = str.getBytes();
+
+
+ int count = 1000000;
+
+
+ byte[][] arraysx4 = {array,array,array,array};
+ byte[][] arraysx8 = {array,array,array,array,array,array,array,array};
+ byte[][] arraysx16 = {array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array};
+ byte[][] arraysx32 = {array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array};
+
+
+ System.out.println("=================== do MBSHA256 hash test in x4===================");
+ for (int r = 0; r < 5; r++) {
+ System.out.println("------------- round[" + r + "] --------------");
+ long startTS = System.currentTimeMillis();
+ for (int i = 0; i < count; i++) {
+ mbsha256.multiBufferHash(arraysx4);
+ }
+ long elapsedTS = System.currentTimeMillis() - startTS;
+ System.out.println(String.format("SHA256 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f; Total KBPS=%.2f", count, elapsedTS,
+ (count * 1000.00D) / elapsedTS, (count * 1000.00D) / elapsedTS*4));
+ }
+ System.out.println();
+ System.out.println();
+
+ System.out.println("=================== do MBSHA256 hash test in x8===================");
+ for (int r = 0; r < 5; r++) {
+ System.out.println("------------- round[" + r + "] --------------");
+ long startTS = System.currentTimeMillis();
+ for (int i = 0; i < count; i++) {
+ mbsha256.multiBufferHash(arraysx8);
+ }
+ long elapsedTS = System.currentTimeMillis() - startTS;
+ System.out.println(String.format("SHA256 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f; Total KBPS=%.2f", count, elapsedTS,
+ (count * 1000.00D) / elapsedTS, (count * 1000.00D) / elapsedTS*8));
+ }
+ System.out.println();
+ System.out.println();
+
+ System.out.println("=================== do MBSHA256 hash test in x16===================");
+ for (int r = 0; r < 5; r++) {
+ System.out.println("------------- round[" + r + "] --------------");
+ long startTS = System.currentTimeMillis();
+ for (int i = 0; i < count; i++) {
+ mbsha256.multiBufferHash(arraysx16);
+ }
+ long elapsedTS = System.currentTimeMillis() - startTS;
+ System.out.println(String.format("SHA256 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f; Total KBPS=%.2f", count, elapsedTS,
+ (count * 1000.00D) / elapsedTS, (count * 1000.00D) / elapsedTS*16));
+ }
+ System.out.println();
+ System.out.println();
+
+ System.out.println("=================== do MBSHA256 hash test in x32===================");
+ for (int r = 0; r < 5; r++) {
+ System.out.println("------------- round[" + r + "] --------------");
+ long startTS = System.currentTimeMillis();
+ for (int i = 0; i < count; i++) {
+ mbsha256.multiBufferHash(arraysx32);
+ }
+ long elapsedTS = System.currentTimeMillis() - startTS;
+ System.out.println(String.format("SHA256 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f; Total KBPS=%.2f", count, elapsedTS,
+ (count * 1000.00D) / elapsedTS, (count * 1000.00D) / elapsedTS*32));
+ }
+ }
+}
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIRIPEMD160UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIRIPEMD160UtilsTest.java
new file mode 100644
index 00000000..47bb7bda
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIRIPEMD160UtilsTest.java
@@ -0,0 +1,41 @@
+package test.com.jd.blockchain.crypto.jniutils;
+
+import com.jd.blockchain.crypto.jniutils.hash.JNIRIPEMD160Utils;
+
+public class JNIRIPEMD160UtilsTest {
+
+ /* Program entry function */
+ public static void main(String args[]) {
+ byte[] array1 = "abc".getBytes();
+ byte[] array2;
+ JNIRIPEMD160Utils ripemd160 = new JNIRIPEMD160Utils();
+ array2 = ripemd160.hash(array1);
+
+ System.out.print("JAVA to C : ");
+ for (byte anArray1 : array1) {
+ System.out.print(anArray1 + " ");
+ }
+ System.out.println();
+ System.out.print("C to JAVA : ");
+ for (byte anArray2 : array2) {
+ System.out.print(anArray2 + " ");
+ }
+ System.out.println();
+
+ String str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
+ byte[] array = str.getBytes();
+ int count = 1000000;
+
+ 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++) {
+ ripemd160.hash(array);
+ }
+ 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));
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNISHA256UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNISHA256UtilsTest.java
new file mode 100644
index 00000000..10e040f8
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNISHA256UtilsTest.java
@@ -0,0 +1,41 @@
+package test.com.jd.blockchain.crypto.jniutils;
+
+import com.jd.blockchain.crypto.jniutils.hash.JNISHA256Utils;
+
+public class JNISHA256UtilsTest {
+
+ /* Program entry function */
+ public static void main(String args[]) {
+ byte[] array1 = "abc".getBytes();
+ byte[] array2;
+ JNISHA256Utils sha256 = new JNISHA256Utils();
+ array2 = sha256.hash(array1);
+ System.out.print("JAVA to C : ");
+ for (byte anArray1 : array1) {
+ System.out.print(anArray1 + " ");
+ }
+ System.out.println();
+ System.out.print("C to JAVA : ");
+ for (byte anArray2 : array2) {
+ System.out.print(anArray2 + " ");
+ }
+ System.out.println();
+
+
+ String str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
+ byte[] array = str.getBytes();
+ int count = 1000000;
+
+ 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++) {
+ sha256.hash(array);
+ }
+ 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));
+ }
+ }
+}
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MyAsymmetricEncryptionTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MyAsymmetricEncryptionTest.java
new file mode 100644
index 00000000..df33e94c
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MyAsymmetricEncryptionTest.java
@@ -0,0 +1,55 @@
+package test.com.jd.blockchain.crypto.performance;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair;
+import com.jd.blockchain.crypto.asymmetric.PrivKey;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+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));
+ }
+ }
+}
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MyHashTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MyHashTest.java
new file mode 100644
index 00000000..0c2733b0
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MyHashTest.java
@@ -0,0 +1,62 @@
+package test.com.jd.blockchain.crypto.performance;
+
+import com.jd.blockchain.crypto.impl.def.hash.RIPEMD160HashFunction;
+import com.jd.blockchain.crypto.impl.def.hash.SHA256HashFunction;
+import com.jd.blockchain.crypto.impl.sm.hash.SM3HashFunction;
+
+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));
+ }
+
+ }
+}
+
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MySignatureTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MySignatureTest.java
new file mode 100644
index 00000000..189d3011
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MySignatureTest.java
@@ -0,0 +1,83 @@
+package test.com.jd.blockchain.crypto.performance;
+
+import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair;
+import com.jd.blockchain.crypto.asymmetric.PrivKey;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.asymmetric.SignatureDigest;
+import com.jd.blockchain.crypto.impl.def.asymmetric.ED25519SignatureFunction;
+import com.jd.blockchain.crypto.impl.sm.asymmetric.SM2CryptoFunction;
+
+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));
+ }
+
+ }
+}
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MySymmetricEncryptionTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MySymmetricEncryptionTest.java
new file mode 100644
index 00000000..82ea11c0
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MySymmetricEncryptionTest.java
@@ -0,0 +1,91 @@
+package test.com.jd.blockchain.crypto.performance;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.impl.def.symmetric.AESSymmetricEncryptionFunction;
+import com.jd.blockchain.crypto.impl.sm.symmetric.SM4SymmetricEncryptionFunction;
+import com.jd.blockchain.crypto.symmetric.SymmetricKey;
+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();
+ SymmetricKey keyAES = (SymmetricKey) 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();
+ SymmetricKey keySM4 = (SymmetricKey) 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));
+ }
+ }
+}
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM2UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM2UtilsTest.java
new file mode 100644
index 00000000..40111b3e
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM2UtilsTest.java
@@ -0,0 +1,105 @@
+package test.com.jd.blockchain.crypto.smutils;
+
+import com.jd.blockchain.crypto.smutils.asymmetric.SM2Utils;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.TestRandomBigInteger;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class SM2UtilsTest {
+
+ @Test
+ public void testGenerateKeyPair() {
+
+ String expectedPrivateKey = "3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8";
+ String expectedPublicKey = "04"+"09f9df311e5421a150dd7d161e4bc5c672179fad1833fc076bb08ff356f35020"+"ccea490ce26775a52dc6ea718cc1aa600aed05fbf35e084a6632f6072da9ad13";
+
+ AsymmetricCipherKeyPair keyPair = SM2Utils.generateKeyPair(new TestRandomBigInteger(expectedPrivateKey, 16));
+ ECPublicKeyParameters ecPub = (ECPublicKeyParameters) keyPair.getPublic();
+ ECPrivateKeyParameters ecPriv = (ECPrivateKeyParameters) keyPair.getPrivate();
+
+ byte[] expectedPrivateKeyBytes = Hex.decode(expectedPrivateKey);
+ byte[] privKeyBytes = ecPriv.getD().toByteArray();
+ assertArrayEquals(expectedPrivateKeyBytes,privKeyBytes);
+
+ byte[] pubKeyBytesX = ecPub.getQ().getAffineXCoord().getEncoded();
+ byte[] pubKeyBytesY = ecPub.getQ().getAffineYCoord().getEncoded();
+ assertEquals(expectedPublicKey,"04"+Hex.toHexString(pubKeyBytesX)+Hex.toHexString(pubKeyBytesY));
+
+ byte[] pubKeyBytes = ecPub.getQ().getEncoded(false);
+ assertArrayEquals(Hex.decode(expectedPublicKey),pubKeyBytes);
+ }
+
+ @Test
+ public void testSign() {
+
+ String expectedPrivateKey = "3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8";
+ String expectedRandomness = "59276E27D506861A16680F3AD9C02DCCEF3CC1FA3CDBE4CE6D54B80DEAC1BC21";
+ String expectedMessage = "message digest";
+ String expectedIdentifier = "ALICE123@YAHOO.COM";
+ String expectedR = "b0e3e7d4ac2178f833ad73fa9d1191e41c76c8bfedb5ad89040ba2e5184bde58";
+ String expectedS = "cc8d096578f7dd2669ac1ac42f7e722bcfa42b9e0be0b1b5df7ca0b53fdd5750";
+
+ byte[] privKeyBytes = Hex.decode(expectedPrivateKey);
+ byte[] messageBytes = expectedMessage.getBytes();
+
+ byte[] signature = SM2Utils.sign(messageBytes,privKeyBytes,new TestRandomBigInteger(expectedRandomness, 16),expectedIdentifier);
+ assertArrayEquals(Hex.decode(expectedR+expectedS),signature);
+ }
+
+ @Test
+ public void testVerify() {
+
+ String expectedPublicKey = "04"+"09f9df311e5421a150dd7d161e4bc5c672179fad1833fc076bb08ff356f35020"+"ccea490ce26775a52dc6ea718cc1aa600aed05fbf35e084a6632f6072da9ad13";
+ String expectedMessage = "message digest";
+ String expectedIdentifier = "ALICE123@YAHOO.COM";
+ String expectedR = "b0e3e7d4ac2178f833ad73fa9d1191e41c76c8bfedb5ad89040ba2e5184bde58";
+ String expectedS = "cc8d096578f7dd2669ac1ac42f7e722bcfa42b9e0be0b1b5df7ca0b53fdd5750";
+
+ byte[] pubKeyBytes = Hex.decode(expectedPublicKey);
+ byte[] messageBytes = expectedMessage.getBytes();
+ byte[] signatureBytes = Hex.decode(expectedR + expectedS);
+
+ boolean isVerified = SM2Utils.verify(messageBytes,pubKeyBytes,signatureBytes,expectedIdentifier);
+ assertTrue(isVerified);
+ }
+
+ @Test
+ public void testEncrypt() {
+
+ String expectedPublicKey = "04"+"09f9df311e5421a150dd7d161e4bc5c672179fad1833fc076bb08ff356f35020"+"ccea490ce26775a52dc6ea718cc1aa600aed05fbf35e084a6632f6072da9ad13";
+ String expectedMessage = "encryption standard";
+ String expectedRandomness = "59276E27D506861A16680F3AD9C02DCCEF3CC1FA3CDBE4CE6D54B80DEAC1BC21";
+ String expectedC1 = "0404ebfc718e8d1798620432268e77feb6415e2ede0e073c0f4f640ecd2e149a73e858f9d81e5430a57b36daab8f950a3c64e6ee6a63094d99283aff767e124df0";
+ String expectedC2 = "21886ca989ca9c7d58087307ca93092d651efa";
+ String expectedC3 = "59983c18f809e262923c53aec295d30383b54e39d609d160afcb1908d0bd8766";
+
+ byte[] pubKeyBytes = Hex.decode(expectedPublicKey);
+ byte[] messageBytes = expectedMessage.getBytes();
+
+ byte[] ciphertext = SM2Utils.encrypt(messageBytes,pubKeyBytes,new TestRandomBigInteger(expectedRandomness, 16));
+ assertArrayEquals(Hex.decode(expectedC1 + expectedC3 + expectedC2),ciphertext);
+
+
+ }
+
+ @Test
+ public void testDecrypt() {
+
+ String expectedPrivateKey = "3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8";
+ String expectedMessage = "encryption standard";
+ String expectedC1 = "0404ebfc718e8d1798620432268e77feb6415e2ede0e073c0f4f640ecd2e149a73e858f9d81e5430a57b36daab8f950a3c64e6ee6a63094d99283aff767e124df0";
+ String expectedC2 = "21886ca989ca9c7d58087307ca93092d651efa";
+ String expectedC3 = "59983c18f809e262923c53aec295d30383b54e39d609d160afcb1908d0bd8766";
+
+ byte[] privKeyBytes = Hex.decode(expectedPrivateKey);
+ byte[] ciphertext = Hex.decode(expectedC1 + expectedC3 + expectedC2);
+
+ byte[] plaintext = SM2Utils.decrypt(ciphertext,privKeyBytes);
+ assertArrayEquals(expectedMessage.getBytes(),plaintext);
+ }
+}
\ No newline at end of file
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM3UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM3UtilsTest.java
new file mode 100644
index 00000000..5d2ff04d
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM3UtilsTest.java
@@ -0,0 +1,33 @@
+package test.com.jd.blockchain.crypto.smutils;
+
+import com.jd.blockchain.crypto.smutils.hash.SM3Utils;
+import org.bouncycastle.util.encoders.Hex;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class SM3UtilsTest {
+
+ private static final int SM3DIGEST_LENGTH = 32;
+
+ @Test
+ public void testHash() {
+
+ String testString1 = "abc";
+ String testString2 = "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
+ String expectedResult1="66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0" ;
+ String expectedResult2="debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732";
+
+ byte[] testString1Bytes = testString1.getBytes();
+ byte[] testString2Bytes = testString2.getBytes();
+ byte[] hash1 = SM3Utils.hash(testString1Bytes);
+ byte[] hash2 = SM3Utils.hash(testString2Bytes);
+ byte[] expectedResult1Bytes = expectedResult1.getBytes();
+ byte[] expectedResult2Bytes = expectedResult2.getBytes();
+ assertEquals(hash1.length, SM3DIGEST_LENGTH);
+ assertEquals(hash2.length, SM3DIGEST_LENGTH);
+ assertArrayEquals(hash1, Hex.decode(expectedResult1Bytes));
+ assertArrayEquals(hash2, Hex.decode(expectedResult2Bytes));
+ }
+
+}
\ No newline at end of file
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM4UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM4UtilsTest.java
new file mode 100644
index 00000000..2137eec4
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM4UtilsTest.java
@@ -0,0 +1,66 @@
+package test.com.jd.blockchain.crypto.smutils;
+
+import com.jd.blockchain.crypto.smutils.symmetric.SM4Utils;
+import org.bouncycastle.util.encoders.Hex;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class SM4UtilsTest {
+
+ private static final int KEY_SIZE = 16;
+ private static final int BLOCK_SIZE = 16;
+
+ @Test
+ public void testGenerateKey() {
+ byte[] key = SM4Utils.generateKey();
+ assertEquals(KEY_SIZE,key.length);
+ }
+
+ @Test
+ public void testEncrypt() {
+
+ String plaintext = "0123456789abcdeffedcba9876543210";
+ String key = "0123456789abcdeffedcba9876543210";
+ String iv = "00000000000000000000000000000000";
+ String expectedCiphertextIn2ndBlock = "681edf34d206965e86b3e94f536e4246";
+
+ byte[] plaintextBytes = Hex.decode(plaintext);
+ byte[] keyBytes = Hex.decode(key);
+ byte[] ivBytes = Hex.decode(iv);
+ byte[] expectedCiphertextIn2ndBlockBytes = Hex.decode(expectedCiphertextIn2ndBlock);
+
+
+ byte[] ciphertextbytes = SM4Utils.encrypt(plaintextBytes,keyBytes,ivBytes);
+
+ assertEquals(BLOCK_SIZE*3,ciphertextbytes.length);
+
+ byte[] ciphertextIn1stBlockBytes = new byte[BLOCK_SIZE];
+ System.arraycopy(ciphertextbytes,0,ciphertextIn1stBlockBytes,0,BLOCK_SIZE);
+ assertArrayEquals(ivBytes,ciphertextIn1stBlockBytes);
+
+ byte[] ciphertextIn2ndBlockBytes = new byte[BLOCK_SIZE];
+ System.arraycopy(ciphertextbytes,BLOCK_SIZE,ciphertextIn2ndBlockBytes,0,BLOCK_SIZE);
+ assertArrayEquals(expectedCiphertextIn2ndBlockBytes,ciphertextIn2ndBlockBytes);
+
+
+ }
+
+ @Test
+ public void testDecrypt() {
+
+ String plaintext = "0123456789abcdeffedcba987654321000112233445566778899";
+ String key = "0123456789abcdeffedcba9876543210";
+ String iv = "0123456789abcdeffedcba9876543210";
+
+ byte[] plaintextBytes = Hex.decode(plaintext);
+ byte[] keyBytes = Hex.decode(key);
+ byte[] ivBytes = Hex.decode(iv);
+
+
+ byte[] ciphertext = SM4Utils.encrypt(plaintextBytes,keyBytes,ivBytes);
+ byte[] decryptedData = SM4Utils.decrypt(ciphertext,keyBytes);
+ assertArrayEquals(plaintextBytes,decryptedData);
+
+ }
+}
\ No newline at end of file
diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/symmetric/SymmetricCryptographyImplTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/symmetric/SymmetricCryptographyImplTest.java
new file mode 100644
index 00000000..37a3ceaf
--- /dev/null
+++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/symmetric/SymmetricCryptographyImplTest.java
@@ -0,0 +1,471 @@
+package test.com.jd.blockchain.crypto.symmetric;
+
+import com.jd.blockchain.crypto.Ciphertext;
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.impl.SymmetricCryptographyImpl;
+import com.jd.blockchain.crypto.symmetric.SymmetricCryptography;
+import com.jd.blockchain.crypto.symmetric.SymmetricEncryptionFunction;
+import com.jd.blockchain.crypto.symmetric.SymmetricKey;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Random;
+
+import static com.jd.blockchain.crypto.CryptoKeyType.PRIV_KEY;
+import static com.jd.blockchain.crypto.CryptoKeyType.SYMMETRIC_KEY;
+import static org.junit.Assert.*;
+
+public class SymmetricCryptographyImplTest {
+
+ @Test
+ public void testGenerateKey() {
+
+ SymmetricCryptography symmetricCrypto = new SymmetricCryptographyImpl();
+
+ //test AES
+ CryptoAlgorithm algorithm = CryptoAlgorithm.AES;
+ verifyGenerateKey(symmetricCrypto,algorithm);
+
+ //test SM4
+ algorithm = CryptoAlgorithm.SM4;
+ verifyGenerateKey(symmetricCrypto,algorithm);
+ }
+
+ private void verifyGenerateKey(SymmetricCryptography symmetricCrypto, CryptoAlgorithm algorithm){
+
+ SymmetricKey symmetricKey= symmetricCrypto.generateKey(algorithm);
+
+ assertNotNull(symmetricKey);
+ assertEquals(algorithm, symmetricKey.getAlgorithm());
+ assertEquals(128/8,symmetricKey.getRawKeyBytes().length);
+
+ byte[] symmetricKeyBytes = symmetricKey.toBytes();
+ //判断密钥数据长度=算法标识长度+密钥掩码长度+原始密钥长度
+ assertEquals(1 + 1 + 128 / 8, symmetricKeyBytes.length);
+
+ assertEquals(algorithm.CODE,symmetricKeyBytes[0]);
+ assertEquals(algorithm,CryptoAlgorithm.valueOf(symmetricKeyBytes[0]));
+ }
+
+ @Test
+ public void testGetSymmetricEncryptionFunction() {
+
+ SymmetricCryptography symmetricCrypto = new SymmetricCryptographyImpl();
+ Random random = new Random();
+
+
+ //test AES
+ CryptoAlgorithm algorithm = CryptoAlgorithm.AES;
+
+ //Case 1: AES with 16 bytes data
+ //刚好一个分组长度,随机生成明文数据
+ byte[] data = new byte[16];
+ random.nextBytes(data);
+ verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 2*16, null);
+
+ //Case 2: AES with 33 bytes data
+ //明文长度大于两倍分组长度,生成的密文是三倍分组长度
+ data = new byte[33];
+ random.nextBytes(data);
+ verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 3*16,null);
+
+ //Case 3: AES with 3 bytes data
+ //明文长度小于分组长度,生成的密文是一倍分组长度
+ data = new byte[3];
+ random.nextBytes(data);
+ verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 16,null);
+
+ //Case 4: AES with 0 bytes data
+ //明文长度小于分组长度,生成的密文是一倍分组长度
+ data = new byte[0];
+ random.nextBytes(data);
+ verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 16,null);
+
+ //Case 5 AES with null
+ //明文为空,可以捕获到异常异常
+ data = null;
+ verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 16,IllegalArgumentException.class);
+
+
+ //test ED25519
+ algorithm = CryptoAlgorithm.ED25519;
+ data = new byte[16];
+ random.nextBytes(data);
+ verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 16,IllegalArgumentException.class);
+
+
+ //test SM4
+ algorithm = CryptoAlgorithm.SM4;
+
+ //Case 1: SM4 with 16 bytes data
+ data = new byte[16];
+ random.nextBytes(data);
+ //密文长度 = IV长度 + 真实密文长度
+ verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 3*16, null);
+
+ //Case 2: SM4 with 33 bytes data
+ data = new byte[33];
+ random.nextBytes(data);
+ verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 4*16,null);
+
+ //Case 3: SM4 with 3 bytes data
+ data = new byte[3];
+ random.nextBytes(data);
+ verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 2*16,null);
+
+ //Case 4: SM4 with 0 bytes data
+ data = new byte[0];
+ random.nextBytes(data);
+ verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 2*16,null);
+
+ //Case 5 SM4 with null
+ data = null;
+ verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 16,IllegalArgumentException.class);
+ }
+
+ //不同明文输入下,用来简化加解密过程的method
+ private void verifyGetSymmetricEncryptionFunction(SymmetricCryptography symmetricCrypto, CryptoAlgorithm algorithm,
+ byte[] data, int expectedCiphertextLength, Class> expectedException){
+
+ //初始化一个异常
+ Exception actualEx = null;
+
+ try {
+ SymmetricEncryptionFunction sef = symmetricCrypto.getSymmetricEncryptionFunction(algorithm);
+ //验证获取的算法实例非空
+ assertNotNull(sef);
+
+ SymmetricKey symmetricKey = (SymmetricKey) sef.generateSymmetricKey();
+
+ //验证SymmetricKey的getAlgorithm方法
+ assertEquals(algorithm, symmetricKey.getAlgorithm());
+ //验证SymmetricKey的getRawKeyBytes方法
+ assertEquals(16, symmetricKey.getRawKeyBytes().length);
+ //验证SymmetricKey的toBytes方法
+ assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{SYMMETRIC_KEY.CODE},symmetricKey.getRawKeyBytes()), symmetricKey.toBytes());
+
+
+ Ciphertext ciphertext = sef.encrypt(symmetricKey,data);
+
+ //Ciphertext中算法标识与入参算法一致
+ assertEquals(algorithm, ciphertext.getAlgorithm());
+ //验证原始密文长度与预期长度一致
+ assertEquals(expectedCiphertextLength, ciphertext.getRawCiphertext().length);
+ //验证密文数据长度=算法标识长度+预期长度
+ byte[] ciphertextBytes = ciphertext.toBytes();
+ assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},ciphertext.getRawCiphertext()), ciphertextBytes);
+
+
+ //验证equal
+ assertTrue(ciphertext.equals(ciphertext));
+ assertEquals(ciphertext.hashCode(),ciphertext.hashCode());
+
+ //验证SymmetricEncryptionFunction的decrypt
+ assertArrayEquals(data, sef.decrypt(symmetricKey,ciphertext));
+
+ //测试SymmetricEncryptionFunction的输入输出流的加解密方法
+ InputStream inPlaintext = new ByteArrayInputStream(data);
+ //16字节的明文输入,将会产生32字节的密文
+ OutputStream outCiphertext = new ByteArrayOutputStream(ciphertext.toBytes().length);
+ InputStream inCiphertext = new ByteArrayInputStream(ciphertext.toBytes());
+ OutputStream outPlaintext = new ByteArrayOutputStream(data.length);
+ sef.encrypt(symmetricKey, inPlaintext, outCiphertext);
+ sef.decrypt(symmetricKey, inCiphertext, outPlaintext);
+
+ //验证SymmetricEncryptionFunction的supportCiphertext方法
+ assertTrue(sef.supportCiphertext(ciphertextBytes));
+
+ //验证SymmetricEncryptionFunction的resolveCiphertext方法
+ assertEquals(ciphertext, sef.resolveCiphertext(ciphertextBytes));
+
+ //验证SymmetricEncryptionFunction的supportSymmetricKey方法
+ assertTrue(sef.supportSymmetricKey(symmetricKey.toBytes()));
+
+ //验证SymmetricEncryptionFunction的resolveSymmetricKey方法
+ assertEquals(symmetricKey, sef.resolveSymmetricKey(symmetricKey.toBytes()));
+
+ //验证SymmetricEncryptionFunction的getAlgorithm
+ assertEquals(algorithm, sef.getAlgorithm());
+
+ } catch (Exception e){
+ actualEx = e;
+ }
+
+ if (expectedException == null) {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testDecrypt() {
+
+ SymmetricCryptography symmetricCrypto = new SymmetricCryptographyImpl();
+ Random randomData = new Random();
+ Random randomKey = new Random();
+
+
+ //test AES
+ CryptoAlgorithm algorithm = CryptoAlgorithm.AES;
+ SymmetricEncryptionFunction sef = symmetricCrypto.getSymmetricEncryptionFunction(algorithm);
+
+ byte[] data = new byte[16];
+ randomData.nextBytes(data);
+ byte[] key = new byte[16];
+ randomKey.nextBytes(key);
+
+ SymmetricKey symmetricKey = new SymmetricKey(algorithm, key);
+ byte[] ciphertextBytes = sef.encrypt(symmetricKey,data).toBytes();
+
+ verifyDecrypt(symmetricCrypto, algorithm, key, data, ciphertextBytes, null);
+
+ //密钥的算法标识与密文的算法标识不一致情况
+ verifyDecrypt(symmetricCrypto, CryptoAlgorithm.SM4, key, data, ciphertextBytes, IllegalArgumentException.class);
+
+ //密文末尾两个字节丢失情况下,抛出异常
+ byte[] truncatedCiphertextBytes = new byte[ciphertextBytes.length-2];
+ System.arraycopy(ciphertextBytes,0,truncatedCiphertextBytes,0,truncatedCiphertextBytes.length);
+ verifyDecrypt(symmetricCrypto, algorithm, key, data, truncatedCiphertextBytes, IllegalArgumentException.class);
+
+ byte[] ciphertextBytesWithWrongAlgCode = ciphertextBytes;
+ ciphertextBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyDecrypt(symmetricCrypto,algorithm,key,data,ciphertextBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ ciphertextBytes = null;
+ verifyDecrypt(symmetricCrypto,algorithm,key,data,ciphertextBytes,NullPointerException.class);
+
+
+ //test SM4
+ algorithm = CryptoAlgorithm.SM4;
+ sef = symmetricCrypto.getSymmetricEncryptionFunction(algorithm);
+ symmetricKey = new SymmetricKey(algorithm, key);
+ ciphertextBytes = sef.encrypt(symmetricKey,data).toBytes();
+
+ verifyDecrypt(symmetricCrypto, algorithm, key, data, ciphertextBytes, null);
+
+ //密钥的算法标识与密文的算法标识不一致情况
+ verifyDecrypt(symmetricCrypto, CryptoAlgorithm.AES, key, data, ciphertextBytes, IllegalArgumentException.class);
+
+ //密文末尾两个字节丢失情况下,抛出异常
+ truncatedCiphertextBytes = new byte[ciphertextBytes.length-2];
+ System.arraycopy(ciphertextBytes,0,truncatedCiphertextBytes,0,truncatedCiphertextBytes.length);
+ verifyDecrypt(symmetricCrypto, algorithm, key, data, truncatedCiphertextBytes, IllegalArgumentException.class);
+
+ ciphertextBytesWithWrongAlgCode = ciphertextBytes;
+ ciphertextBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyDecrypt(symmetricCrypto,algorithm,key,data,ciphertextBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ ciphertextBytes = null;
+ verifyDecrypt(symmetricCrypto,algorithm,key,data,ciphertextBytes,NullPointerException.class);
+ }
+
+ private void verifyDecrypt(SymmetricCryptography symmetricCrypto, CryptoAlgorithm algorithm,
+ byte[] key, byte[] data, byte[] ciphertextBytes, Class> expectedException) {
+
+ Exception actualEx = null;
+
+ try {
+ SymmetricKey symmetricKey = new SymmetricKey(algorithm,key);
+
+ byte[] plaintext = symmetricCrypto.decrypt(symmetricKey.toBytes(), ciphertextBytes);
+
+ //解密后的明文与初始的明文一致
+ assertArrayEquals(data,plaintext);
+ }
+ catch (Exception e){
+ actualEx = e;
+ }
+
+ if (expectedException == null) {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testResolveCiphertext() {
+
+ SymmetricCryptography symmetricCrypto = new SymmetricCryptographyImpl();
+ Random randomData = new Random();
+ Random randomKey = new Random();
+
+ //test AES
+ CryptoAlgorithm algorithm = CryptoAlgorithm.AES;
+ SymmetricEncryptionFunction sef = symmetricCrypto.getSymmetricEncryptionFunction(algorithm);
+
+ byte[] data = new byte[16];
+ randomData.nextBytes(data);
+ byte[] key = new byte[16];
+ randomKey.nextBytes(key);
+
+ SymmetricKey symmetricKey = new SymmetricKey(algorithm, key);
+ byte[] ciphertextBytes = sef.encrypt(symmetricKey,data).toBytes();
+ verifyResolveCiphertext(symmetricCrypto, algorithm, ciphertextBytes, null);
+
+ byte[] truncatedCiphertextBytes = new byte[ciphertextBytes.length-2];
+ System.arraycopy(ciphertextBytes,0,truncatedCiphertextBytes,0,truncatedCiphertextBytes.length);
+ verifyResolveCiphertext(symmetricCrypto,algorithm,truncatedCiphertextBytes,IllegalArgumentException.class);
+
+ byte[] ciphertextBytesWithWrongAlgCode = ciphertextBytes;
+ ciphertextBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyResolveCiphertext(symmetricCrypto,algorithm,ciphertextBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ ciphertextBytes = null;
+ verifyResolveCiphertext(symmetricCrypto,algorithm,ciphertextBytes,NullPointerException.class);
+
+
+ //test SM4
+ algorithm = CryptoAlgorithm.SM4;
+ sef = symmetricCrypto.getSymmetricEncryptionFunction(algorithm);
+
+ symmetricKey = new SymmetricKey(algorithm, key);
+ ciphertextBytes = sef.encrypt(symmetricKey,data).toBytes();
+
+ verifyResolveCiphertext(symmetricCrypto, algorithm, ciphertextBytes, null);
+
+ truncatedCiphertextBytes = new byte[ciphertextBytes.length-2];
+ System.arraycopy(ciphertextBytes,0,truncatedCiphertextBytes,0,truncatedCiphertextBytes.length);
+ verifyResolveCiphertext(symmetricCrypto,algorithm,truncatedCiphertextBytes,IllegalArgumentException.class);
+
+ ciphertextBytesWithWrongAlgCode = ciphertextBytes;
+ ciphertextBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyResolveCiphertext(symmetricCrypto,algorithm,ciphertextBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ ciphertextBytes = null;
+ verifyResolveCiphertext(symmetricCrypto,algorithm,ciphertextBytes,NullPointerException.class);
+ }
+
+ private void verifyResolveCiphertext(SymmetricCryptography symmetricCrypto, CryptoAlgorithm algorithm, byte[] ciphertextBytes,
+ Class> expectedException) {
+
+ Exception actualEx = null;
+
+ try {
+ Ciphertext ciphertext = symmetricCrypto.resolveCiphertext(ciphertextBytes);
+
+ assertNotNull(ciphertext);
+
+ assertEquals(algorithm, ciphertext.getAlgorithm());
+
+ assertEquals(0, ciphertext.getRawCiphertext().length % 16);
+
+ assertArrayEquals(ciphertextBytes, ciphertext.toBytes());
+ }
+ catch (Exception e){
+ actualEx = e;
+ }
+
+ if (expectedException == null) {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testTryResolveCiphertext() {
+ }
+
+
+
+ @Test
+ public void testResolveSymmetricKey() {
+
+ SymmetricCryptography symmetricCrypto = new SymmetricCryptographyImpl();
+
+ //test AES
+ CryptoAlgorithm algorithm = CryptoAlgorithm.AES;
+
+ Random randomKey = new Random();
+ byte[] key = new byte[16];
+ randomKey.nextBytes(key);
+
+ byte[] symmetricKeyBytes = BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{SYMMETRIC_KEY.CODE},key);
+ verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytes,null);
+
+ byte[] truncatedSymmetricKeyBytes = new byte[symmetricKeyBytes.length-2];
+ System.arraycopy(symmetricKeyBytes,0,truncatedSymmetricKeyBytes,0,truncatedSymmetricKeyBytes.length);
+ verifyResolveSymmetricKey(symmetricCrypto,algorithm,truncatedSymmetricKeyBytes,IllegalArgumentException.class);
+
+ byte[] symmetricKeyBytesWithWrongAlgCode = symmetricKeyBytes;
+ symmetricKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ byte[] symmetricKeyBytesWithWrongKeyType= symmetricKeyBytes;
+ System.arraycopy(symmetricKeyBytes,0,symmetricKeyBytesWithWrongKeyType,0,symmetricKeyBytesWithWrongKeyType.length);
+ symmetricKeyBytesWithWrongKeyType[1] = PRIV_KEY.CODE;
+ verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytesWithWrongKeyType,IllegalArgumentException.class);
+
+ symmetricKeyBytes = null;
+ verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytes,NullPointerException.class);
+
+
+ //test SM4
+ algorithm = CryptoAlgorithm.SM4;
+ symmetricKeyBytes = BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{SYMMETRIC_KEY.CODE},key);
+
+ verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytes,null);
+
+ truncatedSymmetricKeyBytes = new byte[symmetricKeyBytes.length-2];
+ System.arraycopy(symmetricKeyBytes,0,truncatedSymmetricKeyBytes,0,truncatedSymmetricKeyBytes.length);
+ verifyResolveSymmetricKey(symmetricCrypto,algorithm,truncatedSymmetricKeyBytes,IllegalArgumentException.class);
+
+ symmetricKeyBytesWithWrongAlgCode = symmetricKeyBytes;
+ symmetricKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE;
+ verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytesWithWrongAlgCode,IllegalArgumentException.class);
+
+ symmetricKeyBytesWithWrongKeyType= symmetricKeyBytes;
+ System.arraycopy(symmetricKeyBytes,0,symmetricKeyBytesWithWrongKeyType,0,symmetricKeyBytesWithWrongKeyType.length);
+ symmetricKeyBytesWithWrongKeyType[1] = PRIV_KEY.CODE;
+ verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytesWithWrongKeyType,IllegalArgumentException.class);
+
+ symmetricKeyBytes = null;
+ verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytes,NullPointerException.class);
+ }
+
+ private void verifyResolveSymmetricKey(SymmetricCryptography symmetricCrypto, CryptoAlgorithm algorithm, byte[] symmetricKeyBytes,
+ Class> expectedException) {
+
+ Exception actualEx = null;
+
+ try {
+ SymmetricKey symmetricKey = symmetricCrypto.resolveSymmetricKey(symmetricKeyBytes);
+
+ assertNotNull(symmetricKey);
+
+ assertEquals(algorithm, symmetricKey.getAlgorithm());
+
+ assertEquals(16, symmetricKey.getRawKeyBytes().length);
+
+ assertArrayEquals(symmetricKeyBytes, symmetricKey.toBytes());
+ }
+ catch (Exception e){
+ actualEx = e;
+ }
+
+ if (expectedException == null) {
+ assertNull(actualEx);
+ }
+ else {
+ assertNotNull(actualEx);
+ assertTrue(expectedException.isAssignableFrom(actualEx.getClass()));
+ }
+ }
+
+ @Test
+ public void testTryResolveSymmetricKey() {
+ }
+}
diff --git a/source/crypto/pom.xml b/source/crypto/pom.xml
new file mode 100644
index 00000000..15e92fd9
--- /dev/null
+++ b/source/crypto/pom.xml
@@ -0,0 +1,18 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ jdchain-root
+ 0.8.2.RELEASE
+
+ crypto
+ pom
+
+
+ crypto-framework
+ crypto-adv
+
+
+
\ No newline at end of file
diff --git a/source/deployment/deployment-gateway/pom.xml b/source/deployment/deployment-gateway/pom.xml
new file mode 100644
index 00000000..7dda468f
--- /dev/null
+++ b/source/deployment/deployment-gateway/pom.xml
@@ -0,0 +1,60 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ deployment
+ 0.8.2.RELEASE
+
+ deployment-gateway
+
+
+
+ com.jd.blockchain
+ gateway
+ ${project.version}
+
+
+ com.jd.blockchain
+ consensus-bftsmart
+ ${project.version}
+
+
+
+ com.jd.blockchain
+ consensus-mq
+ ${project.version}
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ make-assembly
+ package
+
+ single
+
+
+ jdchain-gateway
+
+ src/main/resources/assembly.xml
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/deployment/deployment-gateway/src/main/java/com/jd/blockchain/gateway/boot/GatewayBooter.java b/source/deployment/deployment-gateway/src/main/java/com/jd/blockchain/gateway/boot/GatewayBooter.java
new file mode 100644
index 00000000..2cec1a5f
--- /dev/null
+++ b/source/deployment/deployment-gateway/src/main/java/com/jd/blockchain/gateway/boot/GatewayBooter.java
@@ -0,0 +1,57 @@
+package com.jd.blockchain.gateway.boot;
+
+import com.jd.blockchain.gateway.GatewayServerBooter;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.lang.management.ManagementFactory;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class GatewayBooter {
+
+ public static void main(String[] args) {
+ try {
+ writePID();
+ GatewayServerBooter.main(args);
+ } catch (Exception e) {
+ System.err.println("Error!!! --[" + e.getClass().getName() + "] " + e.getMessage());
+ }
+ }
+
+ private static final void writePID() throws Exception {
+ URL url = GatewayBooter.class
+ .getProtectionDomain()
+ .getCodeSource()
+ .getLocation();
+ String currPath = java.net.URLDecoder.decode(url.getPath(), "UTF-8");
+ if (currPath.contains("!/")) {
+ currPath = currPath.substring(5, currPath.indexOf("!/"));
+ }
+ if (currPath.endsWith(".jar")) {
+ currPath = currPath.substring(0, currPath.lastIndexOf("/") + 1);
+ }
+ System.out.printf("currentPath = %s \r\n", currPath);
+ File file = new File(currPath);
+ String homeDir = file.getParent();
+ String pidFilePath = homeDir + File.separator + "bin" + File.separator + "PID.log";
+ File pidFile = new File(pidFilePath);
+ if (!pidFile.exists()) {
+ pidFile.createNewFile();
+ }
+ String name = ManagementFactory.getRuntimeMXBean().getName();
+ String pid = name.split("@")[0];
+ List bootInfos = new ArrayList<>();
+ bootInfos.add("JDChain gateway starts to boot ......\r\n");
+ bootInfos.add(String.format("GW_BOOT_TIME = [%s] \r\n", new Date().toString()));
+ bootInfos.add(String.format("GW_BOOT_PID = [%s] \r\n", pid));
+ try (FileOutputStream outputStream = new FileOutputStream(pidFile)) {
+ for (String bootInfo : bootInfos) {
+ outputStream.write(bootInfo.getBytes());
+ }
+ outputStream.flush();
+ }
+ }
+}
diff --git a/source/deployment/deployment-gateway/src/main/resources/assembly.xml b/source/deployment/deployment-gateway/src/main/resources/assembly.xml
new file mode 100644
index 00000000..39811c14
--- /dev/null
+++ b/source/deployment/deployment-gateway/src/main/resources/assembly.xml
@@ -0,0 +1,35 @@
+
+
+ ${project.version}
+
+ zip
+
+ false
+
+
+ src/main/resources/scripts
+ bin
+
+
+ src/main/resources/config
+ config
+
+
+ src/main/resources/docs
+ docs
+
+
+
+
+ false
+ true
+ lib
+
+ com.jd.blockchain:deployment-gateway
+
+
+
+
\ No newline at end of file
diff --git a/source/deployment/deployment-gateway/src/main/resources/config/gateway.conf b/source/deployment/deployment-gateway/src/main/resources/config/gateway.conf
new file mode 100644
index 00000000..79902cc6
--- /dev/null
+++ b/source/deployment/deployment-gateway/src/main/resources/config/gateway.conf
@@ -0,0 +1,28 @@
+#网关的HTTP服务地址;
+http.host=127.0.0.1
+#网关的HTTP服务端口;
+http.port=8081
+#网关的HTTP服务上下文路径,可选;
+#http.context-path=
+
+#共识节点的服务地址;
+peer.host=127.0.0.1
+#共识节点的服务端口;
+peer.port=7080
+#共识节点的服务是否启用安全证书;
+peer.secure=false
+#共识节点的服务提供解析器
+peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider
+
+#数据检索服务对应URL
+#若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示
+data.retrieval.url=http://192.168.1.1:10001
+
+#默认公钥的内容(Base58编码数据);
+keys.default.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
+#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一;
+keys.default.privkey-path=
+#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一;
+keys.default.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY
+#默认私钥的解码密码;
+keys.default.privkey-password=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY
\ No newline at end of file
diff --git a/source/deployment/deployment-gateway/src/main/resources/docs/api_doc_cn_1.3.MD b/source/deployment/deployment-gateway/src/main/resources/docs/api_doc_cn_1.3.MD
new file mode 100644
index 00000000..f9e315e0
--- /dev/null
+++ b/source/deployment/deployment-gateway/src/main/resources/docs/api_doc_cn_1.3.MD
@@ -0,0 +1,1794 @@
+# 京东区块链浏览器API文档参考 V_1.3
+
+## 1 API调用说明
+
+该文档内的所有api的调用成功和失败均按照以下规则
+
+### 1.1 成功
+
+```json
+{
+ "data": ...,
+ "success": true
+}
+```
+
+说明
+
+ - success 值为 true 表明api调用成功
+ - data 为返回的数据,具体数据类型参考具体的api说明
+
+### 1.2 失败
+
+```json
+{
+ "error": {
+ "errorCode": 5000,
+ "errorMessage": "未预期的异常! --Unsupported access ledger[6Gw3cK4uazegy4HjoaM81ck9NgYLNoKyBMb7a1TK1jt3d] !"
+ },
+ "success": false
+}
+```
+
+说明
+
+ - success 值为 false 表明api调用成功
+ - errorCode 为异常代码
+ - errorMessage 为错误提示
+
+## 2 账本
+
+### 2.1 获取账本总数
+
+```http
+GET /ledgers/count
+```
+
+#### 参数
+无
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/count
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 2,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|账本总数|
+
+
+### 2.2 获取账本列表
+
+```http
+GET /ledgers?fromIndex={start_index}&count={count}
+```
+
+#### 参数
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|query|start_index|否|查询账本的起始序号,默认为0|数字
+|query|count|否|查询返回账本的数量限制,默认最大限制为100,小于0或大于100均返回最大可返回结果集|数字
+
+
+#### 请求实例
+```http
+http://localhost/ledgers?fromIndex=0&count=-1
+```
+
+#### 返回实例
+
+```json
+{
+ "data": [
+ {
+ "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs"
+ }
+ ],
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|账本哈希列表|
+|value|账户哈希|
+
+### 2.3 获取账本详细信息
+
+```http
+GET /ledgers/{ledger}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs
+```
+
+#### 返回实例
+
+```json
+{
+ "data": {
+ "hash": {
+ "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs"
+ },
+ "latestBlockHash": {
+ "value": "67XsKWgqZTBz1NsytKGpyNWHMbMRENWcBj8PEDYQnWiDL"
+ },
+ "latestBlockHeight": 66
+ },
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|账本信息|
+|hash.value|账本哈希|
+|latestBlockHash.value|最新区块哈希
+|latestBlockHeight|账本高度
+
+
+### 2.4 获取账本成员总数
+
+```http
+GET /ledgers/{ledger}/participants/count
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/participants/count
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 4,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|账本成员总数|
+
+
+### 2.5 获取账本成员列表
+
+```http
+GET /ledgers/{ledger}/participants?fromIndex={start_index}&count={count}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+|query|start_index|否|查询成员起始序号,默认为0|数字
+|query|count|否|查询成员返回数量,默认最大返回100,小于0或大于100均返回最大可返回结果集|数字
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/participants?fromIndex=0&count=-1
+```
+
+#### 返回实例
+
+```json
+{
+ "data": [
+ {
+ "address": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522",
+ "name": "jd.com",
+ "id": 0,
+ "pubKey": {
+ "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
+ }
+ },
+ {
+ "address": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha",
+ "name": "at.com",
+ "id": 1,
+ "pubKey": {
+ "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
+ }
+ },
+ {
+ "address": "5SmMWsqV2kbgrRMjyQFtSq1wvYuPzeRVepHG",
+ "name": "bt.com",
+ "id": 2,
+ "pubKey": {
+ "value": "mb4AtiGAH7vtPufMDuap2oca2Ww9X6KTkp59Eh5nZjXA5H"
+ }
+ },
+ {
+ "address": "5Sm5QFyvN1dVB4GHFxWhDCp8vsJbNkdx31Ds",
+ "name": "xt.com",
+ "id": 3,
+ "pubKey": {
+ "value": "mb7pGhmmjqYUhxrJJ57C1YxXr9h1AWXv8QVosETyuLhVvH"
+ }
+ }
+ ],
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|id|成员唯一标识|
+|name|成员名称|
+|address|成员地址|
+|pubKey.value|成员公钥|
+
+## 3 区块
+
+### 3.1 获取最新区块
+
+```http
+GET /ledgers/{ledger}/blocks/latest
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/latest
+```
+
+#### 返回实例
+
+```json
+{
+ "data": {
+ "ledgerHash": {
+ "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs"
+ },
+ "previousHash": {
+ "value": "6EJZnMc9464DCSU2kgi96RyngEv8YeEfVoJNhH3yZ2v5T"
+ },
+ "transactionSetHash": {
+ "value": "6LmZtDpMM7xE8FPChACEmLj1PLhfaoVM2rEHRsrV3ohPN"
+ },
+ "userAccountSetHash": {
+ "value": "67jx7SctrwdSczxxuYjwBocA8fER7V8qcRZUzWamSav5p"
+ },
+ "contractAccountSetHash": {
+ "value": "67ftaBhPDez24NEB9wiiTM3SNcn1XFz5rb7boYhpbbLXN"
+ },
+ "adminAccountHash": {
+ "value": "69KEFp9m5iFyAiyGmJ2qPcVxuT79gMChMf9JkStBZe8aa"
+ },
+ "dataAccountSetHash": {
+ "value": "6LB9gosVWEPG3uvWXkxTcWq22mcwMHVehbiXkavFtr5fZ"
+ },
+ "hash": {
+ "value": "67XsKWgqZTBz1NsytKGpyNWHMbMRENWcBj8PEDYQnWiDL"
+ },
+ "height": 66
+ },
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|hash|区块哈希|
+|ledgerHash|账本哈希|
+|previousHash|前置区块哈希|
+|transactionSetHash|交易集哈希|
+|userAccountSetHash|用户集哈希|
+|contractAccountSetHash|合约集哈希|
+|adminAccountHash|管理员集哈希|
+|dataAccountSetHash|数据账户集哈希|
+
+### 3.2 根据区块哈希获取区块详细信息
+
+```http
+GET /ledgers/{ledger}/blocks/hash/{block_hash}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串|
+|path|block_hash|是|区块哈希|字符串|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/67XsKWgqZTBz1NsytKGpyNWHMbMRENWcBj8PEDYQnWiDL
+```
+
+#### 返回实例
+
+[参考](#block-detail)
+
+
+### 3.3 根据区块高度获取区块详细信息
+
+```http
+GET /ledgers/{ledger}/blocks/height/{block_height}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串|
+|path|block_height|是|区块高度|数字|
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/height/66
+```
+
+#### 返回实例
+
+[参考](#block-detail)
+
+
+### 3.4 根据哈希查询区块总数
+
+```http
+ GET /ledgers/{ledger}/blocks/count/search?keyword={keyword}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型|
+|---|---|---|---|---|
+|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string|
+|**query**|**keyword**|是| 区块哈希的全部或者一部分|string|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/count/search?keyword=6D5M
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 26,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|查询到的区块总数|
+
+### 3.5 根据哈希查询区块
+
+```http
+ GET /ledgers/{ledger}/blocks/search?keyword={keyword}&fromIndex={start_index}&count={count}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型|
+|---|---|---|---|---|
+|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string|
+|**query**|**keyword**|是| 区块哈希的全部或者一部分|string|
+|**query**|**start_index**|否| 查询区块结果起始序号,默认为0|string|
+|**query**|**count**|否| 查询区块结果返回数量,默认最大值为100,小于0或大于100均返回最大可返回结果集|string|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/search?keyword=6D5M&fromIndex=0&count=-1
+```
+
+
+#### 返回实例
+
+```json
+{
+ "data": {
+ "blocks": [
+ {
+ "hash": "6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb"
+ }
+ ]
+ },
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|blocks|查询到的区块列表|
+|hash|区块哈希值|
+|height|区块高度|
+|txCount|区块内交易数量|
+
+## 4 交易
+
+### 4.1 获取账本交易总数
+
+```http
+GET /ledgers/{ledger}/txs/count
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/count
+```
+
+##### 返回实例
+
+```json
+{
+ "data": 688,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|交易数量|
+
+### 4.2 根据区块高度查询区块内的交易数量
+
+```http
+GET /ledgers/{ledger}/blocks/height/{block_height}/txs/additional-count
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串|
+|path|block_height|是|区块高度|数字|
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/height/66/txs/additional-count
+
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb/txs/additional-count
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 86,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|交易数量|
+
+
+### 4.3 根据区块哈希查询区块内的交易数量
+
+```http
+GET /ledgers/{ledger}/blocks/hash/{block_hash}/txs/additional-count
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串|
+|path|block_hash|是|区块哈希|字符串|
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb/txs/additional-count
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 86,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|交易数量|
+
+
+### 4.4 获取指定高度的区块交易列表
+
+```http
+GET /ledgers/{ledger}/blocks/height/{height}/txs?fromIndex={start_index}&count={count}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+|path|height|是|区块高度|数字|
+|query|start_index|否|查询交易的起始序号,默认为0|数字|
+|query|count|否|查询返回交易的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/height/66/txs?fromIndex=0&count=-1
+```
+
+#### 返回实例
+
+```json
+{
+ "data": [
+ {
+ "blockHeight": 1,
+ "executionState": "SUCCESS",
+ "transactionContent": {
+ "ledgerHash": {
+ "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs"
+ },
+ "operations": [
+ {
+ "userID": {
+ "address": {
+ "value": "5SmBgzsrnY6u9Y7DgSSkXfTkCgp83hiFin3v"
+ },
+ "pubKey": {
+ "value": "mb5kukaqjWtXyAerfHU1JDtVwabSeBU5c3khMZbNh7R8VJ"
+ }
+ }
+ },
+ {
+ "accountID": {
+ "address": {
+ "value": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha"
+ },
+ "pubKey": {
+ "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
+ }
+ }
+ },
+ {
+ "contractID": {
+ "address": {
+ "value": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha"
+ },
+ "pubKey": {
+ "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
+ }
+ },
+ "chainCode": "----------"
+ },
+ {
+ "contractAddress": {
+ "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
+ },
+ "event": "----------",
+ "args": "----------"
+ },
+ {
+ "writeSet": [{
+ "key": "jdchain",
+ "value": {
+ "type": "TEXT",
+ "value": {
+ "value": "----------"
+ }
+ },
+ "expectedVersion": 0
+ }],
+ "accountAddress": {
+ "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
+ }
+ }
+ ],
+ "hash": {
+ "value": "6BLtM1agb7ERKoN5AJgZKiTjzdS7BpjgzQNYK8ZeDqotA"
+ }
+ },
+ "endpointSignatures": [
+ {
+ "digest": {
+ "value": "42pbfM5YKnf39Gitr4UsjTCzhhnJjwNyi8MnLFYgP4VKewTLzHitzArHEMrCt3hZYUe5ex9XvqtmiCoWpeAbdc31F"
+ },
+ "pubKey": {
+ "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
+ }
+ }
+ ],
+ "nodeSignatures": [
+ {
+ "digest": {
+ "value": "66SQ95SbDaApAJhN2NsFx5sfAQTxsWhMW26D5iPqXc1jZU9rJEhRnqT1nzt62ZAcCvsfrjEsay3MxqXYA5tWPoA2U"
+ },
+ "pubKey": {
+ "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
+ }
+ }
+ ]
+ }
+ ],
+ "success": true
+}
+```
+
+
+说明
+
+|名称|说明|
+|---|---|
+|executionState|交易执行结果|
+|transactionContent.hash|交易的哈希|
+|transactionContent.operations|交易的操作列表|
+|endpointSignatures|终端签名列表|
+|nodeSignatures|节点的签名列表|
+
+### 4.5 获取指定哈希的区块的交易列表
+
+```http
+GET /ledgers/{ledger}/blocks/hash/{block_hash}/txs?fromIndex={start_index}&count={count}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+|path|block_hash|是|区块哈希|字符串|
+|query|start_index|否|查询交易的起始序号,默认为0|数字|
+|query|count|否|查询返回交易的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb/txs?fromIndex=0&count=-1
+```
+
+#### 返回实例
+
+[参考](#tx-list)
+
+
+### 4.6 获取交易详细信息
+
+```http
+GET /ledgers/{ledger}/txs/hash/{tx_hash}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+|path|tx_hash|是|交易哈希|字符串|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/hash/6BLtM1agb7ERKoN5AJgZKiTjzdS7BpjgzQNYK8ZeDqotA
+```
+
+#### 返回实例
+
+```json
+{
+ "data": {
+ "blockHeight": 1,
+ "executionState": "SUCCESS",
+ "transactionContent": {
+ "ledgerHash": {
+ "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs"
+ },
+ "operations": [
+ {
+ "userID": {
+ "address": {
+ "value": "5SmBgzsrnY6u9Y7DgSSkXfTkCgp83hiFin3v"
+ },
+ "pubKey": {
+ "value": "mb5kukaqjWtXyAerfHU1JDtVwabSeBU5c3khMZbNh7R8VJ"
+ }
+ }
+ },
+ {
+ "accountID": {
+ "address": {
+ "value": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha"
+ },
+ "pubKey": {
+ "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
+ }
+ }
+ },
+ {
+ "contractID": {
+ "address": {
+ "value": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha"
+ },
+ "pubKey": {
+ "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
+ }
+ },
+ "chainCode": "----------"
+ },
+ {
+ "contractAddress": {
+ "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
+ },
+ "event": "----------",
+ "args": "----------"
+ },
+ {
+ "writeSet": [{
+ "key": "jdchain",
+ "value": {
+ "type": "TEXT",
+ "value": {
+ "value": "----------"
+ }
+ },
+ "expectedVersion": 0
+ }],
+ "accountAddress": {
+ "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
+ }
+ }
+ ],
+ "hash": {
+ "value": "6BLtM1agb7ERKoN5AJgZKiTjzdS7BpjgzQNYK8ZeDqotA"
+ }
+ },
+ "endpointSignatures": [
+ {
+ "digest": {
+ "value": "42pbfM5YKnf39Gitr4UsjTCzhhnJjwNyi8MnLFYgP4VKewTLzHitzArHEMrCt3hZYUe5ex9XvqtmiCoWpeAbdc31F"
+ },
+ "pubKey": {
+ "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
+ }
+ }
+ ],
+ "nodeSignatures": [
+ {
+ "digest": {
+ "value": "66SQ95SbDaApAJhN2NsFx5sfAQTxsWhMW26D5iPqXc1jZU9rJEhRnqT1nzt62ZAcCvsfrjEsay3MxqXYA5tWPoA2U"
+ },
+ "pubKey": {
+ "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
+ }
+ }
+ ]
+ },
+ "success": true
+}
+```
+
+说明
+
+[参考](#tx-keyword)
+
+
+### 4.7 根据哈希查询交易总数
+
+```http
+ GET /ledgers/{ledgers}/txs/count/search?keyword={keyword}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型|
+|---|---|---|---|---|
+|**path**|**ledgers**|是|所要搜索的账本范围,需要完整的账本哈希|string|
+|**query**|**keyword**|是|交易哈希,签名者公钥,或者节点公钥的全部或者部分的|string|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/search?keyword=6BLt
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 36,
+ "success": true
+}
+```
+
+
+说明
+
+|名称|说明|
+|---|---|
+|data|指定交易数量|
+
+
+### 4.8 根据哈希查询交易
+
+```http
+ GET /ledgers/{ledgers}/txs/search?keyword={keyword}&fromIndex={start_index}&count={count}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型|
+|---|---|---|---|---|
+|**path**|**ledgers**|是|所要搜索的账本范围,需要完整的账本哈希|string|
+|**query**|**keyword**|是|交易哈希,签名者公钥,或者节点公钥的全部或者部分的|string|
+|**query**|**start_index**|否|查询交易的起始序号,默认为0|数字|
+|**query**|**count**|否|查询返回交易的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/search?keyword=6BLt
+```
+
+
+#### 返回
+
+```json
+{
+ "data": {
+ "txs": [
+ {
+ "hash": "6L3ehswCmC1jqBfvGJP9vaPx8qxkLsieu2aRgYepmkiw3"
+ }
+ ]
+ },
+ "success": true
+}
+```
+
+## 5 用户
+
+### 5.1 获取用户总数
+
+```http
+GET /ledgers/{ledger}/users/count
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/count
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 4,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|用户总数|
+
+
+### 5.2 获取用户列表
+
+```http
+GET /ledgers/{ledger}/users?fromIndex={start_index}&count={count}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+|query|start_index|否|查询用户的起始序号,默认为0|数字|
+|query|count|否|查询返回用户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users?fromIndex=0&count=-1
+```
+
+#### 返回实例
+
+```json
+{
+ "data":[{
+ "address": {
+ "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522"
+ },
+ "pubKey": {
+ "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
+ },
+ "rootHash": {
+ "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522"
+ }
+ }],
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|address.value|用户地址|
+|pubKey.value|用户公钥|
+
+
+### 5.3 获取用户详细信息
+
+```http
+GET /ledgers/{ledger}/users/address/{address}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+|path|address|是|用户地址|字符串
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/address/55SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522
+```
+
+#### 返回实例
+
+```json
+{
+ "data": {
+ "address": {
+ "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522"
+ },
+ "pubKey": {
+ "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
+ },
+ "rootHash": {
+ "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522"
+ }
+ },
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|address.value|用户地址|
+|pubKey.value|用户公钥|
+|rootHash.value|用户根Hash|
+
+
+### 5.4 用户查询数量
+
+```http
+ GET /ledgers/{ledger}/users/count/search?keyword={keyword}
+```
+
+#### 说明
+
+用户有公钥和地址两个属性,可以通过公钥或者地址查找特定用户数量,也可以返回全部用户的数量
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型|
+|---|---|---|---|---|
+|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string|
+|**query**|**keyword**|是| 用户的公钥或者地址的全部或者部分|string|
+|**query**|**start_index**|否|查询用户的起始序号,默认为0|数字|
+|**query**|**count**|否|查询返回用户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/count/search?keyword=5Sm
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 4,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|用户数量|
+
+### 5.5 用户查询
+
+```http
+ GET /ledgers/{ledger}/users/search?keyword={keyword}&fromIndex={start_index}&count={count}
+```
+
+#### 说明
+
+用户有公钥和地址两个属性,可以通过公钥或者地址查找特定用户,也可以返回全部用户的列表
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型|
+|---|---|---|---|---|
+|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string|
+|**query**|**keyword**|是| 用户的公钥或者地址的全部或者部分|string|
+|**query**|**start_index**|否|查询用户的起始序号,默认为0|数字|
+|**query**|**count**|否|查询返回用户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/search?keyword=5Sm&fromIndex=0&count=-1
+```
+
+
+#### 返回实例
+
+```json
+{
+ "data": {
+ "users": [
+ {
+ "address": {
+ "value": "5SmAGKgmXyj5VsVvJgHbYCJ67iTizwSkNpw1"
+ },
+ "pubKey": {
+ "value": "mb97eG4bba2EjrgjXYiD9chAstjg4HaNuV5xgCtSHc5TeB"
+ }
+ }
+ ]
+ },
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|address.value|用户地址|
+|pubKey.value|用户公钥|
+|rootHash.value|用户根Hash|
+
+## 6 数据账户
+
+### 6.1 获取账户列表
+
+```http
+GET /ledgers/{ledger}/accounts?fromIndex={start_index}&count={count}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+|query|start_index|否|查询数据账户的起始序号,默认为0|数字|
+|query|count|否|查询返回数据账户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts?fromIndex=0&count=-1
+```
+
+#### 返回实例
+
+```json
+{
+ "data":[{
+ "address": {
+ "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa"
+ },
+ "rootHash": {
+ "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM"
+ },
+ "pubKey": {
+ "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs"
+ }
+ }],
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|address.value|账户地址|
+|pubKey.value|账户公钥|
+|rootHash.value|默克尔树根哈希|
+
+
+### 6.2 获取账户详细信息
+
+```http
+GET /ledgers/{ledger}/accounts/address/{address}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+|path|address|是|账户地址|字符串
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa
+```
+
+#### 返回实例
+
+```json
+{
+ "data": {
+ "address": {
+ "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa"
+ },
+ "rootHash": {
+ "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM"
+ },
+ "pubKey": {
+ "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs"
+ }
+ },
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|address.value|账户地址|
+|pubKey.value|账户公钥|
+|rootHash.value|默克尔树根哈希|
+
+
+### 6.3 获取账户总数
+
+```http
+GET /ledgers/{ledger}/accounts/count
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/count
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 18,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|账户数量|
+
+
+### 6.4 查询数据账户匹配的数量
+
+```http
+GET /ledgers/{ledger}/accounts/count/search?keyword={keyword}
+```
+
+#### 说明
+
+通过账户的公钥和地址的全部或者部分查询特定账户的总数量,也可以通过KV值的Key来查询含有该Key的账户的总数量
+
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|字符串
+|**query**|**keyword**|是|数据账户的公钥或者地址的全部或者部分,或者是KV值的key|字符串
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/count/search?keyword=jd
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 2,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|账户数量|
+
+
+### 6.5 查询数据账户
+
+```http
+ GET /ledgers/{ledger}/accounts/search?keyword={keyword}&fromIndex={start_index}&count={count}
+```
+
+#### 说明
+
+通过账户的公钥和地址的全部或者部分查询特定账户,也可以通过KV值的Key来查询含有该Key的账户
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型|
+|---|---|---|---|---|
+|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string|
+|**query**|**keyword**|是| 数据账户的公钥或者地址的全部或者部分,或者是KV值的key|string|
+|**query**|**start_index**|否|查询数据账户的起始序号,默认为0|数字|
+|**query**|**count**|否|查询返回数据账户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/search?keyword=5Sm5V&fromIndex=0&count=-1
+```
+
+
+#### 返回实例
+
+```json
+{
+ "data": {
+ "accounts": [
+ {
+ "address": {
+ "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa"
+ },
+ "pubKey": {
+ "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs"
+ }
+ }
+ ]
+ },
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|address.value|账户地址|
+|pubKey.value|账户公钥|
+|rootHash.value|数据账户根Hash|
+
+
+### 6.6 获取某数据账户KV总数
+
+```http
+ GET /ledgers/{ledger}/accounts/address/{address}/entries/count
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+|path|address|是|账户地址|字符串
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/entries/count
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 66,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|KV总数|
+
+
+### 6.7 获取某数据账户KV详情
+
+```http
+ GET/POST /ledgers/{ledger}/accounts/address/{address}/entries?fromIndex={start_index}&count={count}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+|path|address|是|账户地址|字符串
+|form|keys|是|key详细内容列表|字符串
+|query|start_index|否|查询数据账户对应KV的起始序号,默认为0|数字|
+|query|count|否|查询返回数据账户对应KV的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+
+> keys说明:
+ 1)keys使用表单方式提交,且keys为需要查询Key的列表,列表中每个Key都需要为完整Key
+ 2)Key提交方式使用GET或POST均可
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/entries
+```
+
+说明:表单提交参数为keys={"jd", "jdchain"}
+
+
+#### 返回实例
+
+```json
+{
+ "data": [
+ {
+ "key": "jd",
+ "version": 0,
+ "type": "TEXT",
+ "value": "www.jd.com"
+ },
+ {
+ "key": "jdchain",
+ "version": 0,
+ "type": "TEXT",
+ "value": "www.blockchain.com"
+ }],
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|key|键|
+|version|版本号|
+|type|value类型|
+|value|值|
+
+### 6.8 查询某数据账户键数量
+
+```http
+ GET /ledgers/{ledger}/accounts/address/{address}/keys/count/search?keyword={keyword}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型|
+|---|---|---|---|---|
+|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string|
+|**path**|**address**|是|所要搜索的数据账户地址,需要完整的数据账户地址|string|
+|**query**|**keyword**|否|键的部分字符,空表示全部|string|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/keys/count/search?keyword=j
+```
+
+
+#### 返回实例
+
+```json
+{
+ "data": 66,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|条件查询键总数|
+
+### 6.9 查询某数据账户键
+
+```http
+ GET /ledgers/{ledger}/accounts/address/{address}/keys/search?keyword={keyword}&fromIndex={start_index}&count={count}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型|
+|---|---|---|---|---|
+|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string|
+|**path**|**address**|是|所要搜索的数据账户地址,需要完整的数据账户地址|string|
+|**query**|**keyword**|否|键的部分字符,空表示全部|string|
+|**query**|**start_index**|否|查询数据账户对应Key的起始序号,默认为0|数字|
+|**query**|**count**|否|查询返回数据账户对应Key的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/keys/search?keyword=j&fromIndex=0&count=-1
+```
+
+
+#### 返回实例
+
+```json
+{
+ "data": [
+ {
+ "key": "jd"
+ },
+ {
+ "key": "jdchain"
+ }],
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|key|键|
+
+## 7 搜索
+
+### 7.1 搜索区块链
+
+```http
+ GET /ledgers/{ledger}/all/search?keyword={keyword}&fromIndex={start_index}&count={count}
+```
+
+#### 说明
+
+通过关键字搜索区块数据,支持区块哈希,交易哈希,用户公钥和地址,合约公钥和地址,数据账户哈希和地址的搜索
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型|
+|---|---|---|---|---|
+|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string|
+|**query**|**keyword**|是|关键字|string|
+|**query**|**start_index**|否|查询匹配结果的起始序号,默认为0|数字|
+|**query**|**count**|否|查询匹配结果的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/all/search?keyword=5Sm5V&fromIndex=0&count=-1
+```
+
+#### 返回实例
+
+```json
+{
+ "message": "OK",
+ "code": 0,
+ "data": {
+ "blocks": ...,
+ "txs": ...,
+ "users": ...,
+ "accounts": ...,
+ "contracts": ...,
+ },
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|blocks|[参考](#query-blocks-result)|
+|txs|[参考](#query-txs-result)|
+|users|[参考](#query-users-result)|
+|accounts|[参考](#query-accounts-result)|
+|contracts|[参考](#query-contracts-result)|
+
+## 8 合约
+
+### 8.1 获取合约列表
+
+```http
+GET /ledgers/{ledger}/contracts?fromIndex={start_index}&count={count}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+|query|start_index|否|查询合约的起始序号,默认为0|数字|
+|query|count|否|查询返回合约的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts?fromIndex=0&count=-1
+```
+
+#### 返回实例
+
+```json
+{
+ "data": [{
+ "address": {
+ "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa"
+ },
+ "rootHash": {
+ "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM"
+ },
+ "pubKey": {
+ "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs"
+ }
+ }],
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|address.value|账户地址|
+|pubKey.value|账户公钥|
+|rootHash.value|默克尔树根哈希|
+
+
+### 8.2 获取合约详细信息
+
+```http
+GET /ledgers/{ledger}/contracts/address/{address}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+|path|address|是|合约地址|字符串
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa
+```
+
+#### 返回实例
+
+```json
+{
+ "data": {
+ "address": {
+ "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa"
+ },
+ "rootHash": {
+ "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM"
+ },
+ "pubKey": {
+ "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs"
+ }
+ },
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|address.value|账户地址|
+|pubKey.value|账户公钥|
+|rootHash.value|默克尔树根哈希|
+
+### 8.3 获取合约总数
+
+```http
+GET /ledgers/{ledger}/contracts/count
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型
+|---|---|---|---|---|
+|path|ledger|是|账本哈希|字符串
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/count
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 27,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|合约数量|
+
+
+### 8.4 查询指定合约数量
+
+```http
+GET /ledgers/{ledger}/contracts/count/search?keyword={keyword}
+```
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型|
+|---|---|---|---|---|
+|**path**|**ledger**|是|所要搜索的账本范围,需要完整的账本哈希|string|
+|**query**|**keyword**|是| 合约的公钥或者地址的全部或者一部分|string|
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/count/search?keyword=5Sm2
+```
+
+#### 返回实例
+
+```json
+{
+ "data": 2,
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|data|合约数量|
+
+### 8.5 合约查询
+
+```http
+ GET /ledgers/{ledger}/contracts/search?keyword={keyword}&fromIndex={start_index}&count={count}
+```
+
+#### 说明
+
+合约有公钥和地址两个属性,可以通过合约的这两个属性查询特定合约,也可以返回一个当前所有合约的列表
+
+#### 参数
+
+|请求类型|名称|是否必需|说明|数据类型|
+|---|---|---|---|---|
+|**path**|**ledger**|是|所要搜索的账本范围,需要完整的账本哈希|string|
+|**query**|**keyword**|是| 合约的公钥或者地址的全部或者一部分|string|
+|**query**|**start_index**|否|查询合约的起始序号,默认为0|数字|
+|**query**|**count**|否|查询返回合约的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字|
+
+
+#### 请求实例
+```http
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/earch?keyword=5Sm2&fromIndex=0&count=-1
+```
+
+
+#### 返回
+
+```json
+{
+ "data": {
+ "contracts": [
+ {
+ "address": {
+ "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa"
+ },
+ "rootHash": {
+ "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM"
+ },
+ "pubKey": {
+ "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs"
+ }
+ }
+ ]
+ },
+ "success": true
+}
+```
+
+说明
+
+|名称|说明|
+|---|---|
+|address.value|合约地址|
+|pubKey.value|合约公钥|
+|rootHash|合约根Hash|
diff --git a/source/deployment/deployment-gateway/src/main/resources/docs/api_doc_cn_1.3.html b/source/deployment/deployment-gateway/src/main/resources/docs/api_doc_cn_1.3.html
new file mode 100644
index 00000000..169ef8b7
--- /dev/null
+++ b/source/deployment/deployment-gateway/src/main/resources/docs/api_doc_cn_1.3.html
@@ -0,0 +1,2665 @@
+
+
+
+
+ api_doc_cn_1.3
+
+
+ 京东区块链浏览器API文档参考 V_1.3
+1 API调用说明
+该文档内的所有api的调用成功和失败均按照以下规则
+1.1 成功
+{
" data " : . . . ,
" success " : true
}
+说明
+- success 值为 true 表明api调用成功
- data 为返回的数据,具体数据类型参考具体的api说明
1.2 失败
+{
" error " : {
" errorCode " : 5000 ,
" errorMessage " : " 未预期的异常! --Unsupported access ledger[6Gw3cK4uazegy4HjoaM81ck9NgYLNoKyBMb7a1TK1jt3d] ! "
} ,
" success " : false
}
+说明
+- success 值为 false 表明api调用成功
- errorCode 为异常代码
- errorMessage 为错误提示
2 账本
+2.1 获取账本总数
+GET /ledgers/count
+参数
+无
+请求实例
+http://localhost/ledgers/count
+返回实例
+{
" data " : 2 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+账本总数
+
+
+
+2.2 获取账本列表
+GET /ledgers?fromIndex={start_index}&count={count}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+query
+start_index
+否
+查询账本的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回账本的数量限制,默认最大限制为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers?fromIndex=0&count=-1
+返回实例
+{
" data " : [
{
" value " : " 657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs "
}
] ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+账本哈希列表
+
+
+value
+账户哈希
+
+
+
+2.3 获取账本详细信息
+GET /ledgers/{ledger}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs
+返回实例
+{
" data " : {
" hash " : {
" value " : " 657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs "
} ,
" latestBlockHash " : {
" value " : " 67XsKWgqZTBz1NsytKGpyNWHMbMRENWcBj8PEDYQnWiDL "
} ,
" latestBlockHeight " : 66
} ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+账本信息
+
+
+hash.value
+账本哈希
+
+
+latestBlockHash.value
+最新区块哈希
+
+
+latestBlockHeight
+账本高度
+
+
+
+2.4 获取账本成员总数
+GET /ledgers/{ledger}/participants/count
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/participants/count
+返回实例
+{
" data " : 4 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+账本成员总数
+
+
+
+2.5 获取账本成员列表
+GET /ledgers/{ledger}/participants?fromIndex={start_index}&count={count}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+query
+start_index
+否
+查询成员起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询成员返回数量,默认最大返回100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/participants?fromIndex=0&count=-1
+返回实例
+{
" data " : [
{
" address " : " 5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522 " ,
" name " : " jd.com " ,
" id " : 0 ,
" pubKey " : {
" value " : " mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS "
}
} ,
{
" address " : " 5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha " ,
" name " : " at.com " ,
" id " : 1 ,
" pubKey " : {
" value " : " mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR "
}
} ,
{
" address " : " 5SmMWsqV2kbgrRMjyQFtSq1wvYuPzeRVepHG " ,
" name " : " bt.com " ,
" id " : 2 ,
" pubKey " : {
" value " : " mb4AtiGAH7vtPufMDuap2oca2Ww9X6KTkp59Eh5nZjXA5H "
}
} ,
{
" address " : " 5Sm5QFyvN1dVB4GHFxWhDCp8vsJbNkdx31Ds " ,
" name " : " xt.com " ,
" id " : 3 ,
" pubKey " : {
" value " : " mb7pGhmmjqYUhxrJJ57C1YxXr9h1AWXv8QVosETyuLhVvH "
}
}
] ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+id
+成员唯一标识
+
+
+name
+成员名称
+
+
+address
+成员地址
+
+
+pubKey.value
+成员公钥
+
+
+
+3 区块
+3.1 获取最新区块
+GET /ledgers/{ledger}/blocks/latest
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/latest
+返回实例
+{
" data " : {
" ledgerHash " : {
" value " : " 657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs "
} ,
" previousHash " : {
" value " : " 6EJZnMc9464DCSU2kgi96RyngEv8YeEfVoJNhH3yZ2v5T "
} ,
" transactionSetHash " : {
" value " : " 6LmZtDpMM7xE8FPChACEmLj1PLhfaoVM2rEHRsrV3ohPN "
} ,
" userAccountSetHash " : {
" value " : " 67jx7SctrwdSczxxuYjwBocA8fER7V8qcRZUzWamSav5p "
} ,
" contractAccountSetHash " : {
" value " : " 67ftaBhPDez24NEB9wiiTM3SNcn1XFz5rb7boYhpbbLXN "
} ,
" adminAccountHash " : {
" value " : " 69KEFp9m5iFyAiyGmJ2qPcVxuT79gMChMf9JkStBZe8aa "
} ,
" dataAccountSetHash " : {
" value " : " 6LB9gosVWEPG3uvWXkxTcWq22mcwMHVehbiXkavFtr5fZ "
} ,
" hash " : {
" value " : " 67XsKWgqZTBz1NsytKGpyNWHMbMRENWcBj8PEDYQnWiDL "
} ,
" height " : 66
} ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+hash
+区块哈希
+
+
+ledgerHash
+账本哈希
+
+
+previousHash
+前置区块哈希
+
+
+transactionSetHash
+交易集哈希
+
+
+userAccountSetHash
+用户集哈希
+
+
+contractAccountSetHash
+合约集哈希
+
+
+adminAccountHash
+管理员集哈希
+
+
+dataAccountSetHash
+数据账户集哈希
+
+
+
+3.2 根据区块哈希获取区块详细信息
+GET /ledgers/{ledger}/blocks/hash/{block_hash}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+path
+block_hash
+是
+区块哈希
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/67XsKWgqZTBz1NsytKGpyNWHMbMRENWcBj8PEDYQnWiDL
+返回实例
+参考
+3.3 根据区块高度获取区块详细信息
+GET /ledgers/{ledger}/blocks/height/{block_height}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+path
+block_height
+是
+区块高度
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/height/66
+返回实例
+参考
+3.4 根据哈希查询区块总数
+ GET /ledgers/{ledger}/blocks/count/search?keyword={keyword}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+所要搜索的账本,需要完整的账本哈希
+string
+
+
+query
+keyword
+是
+区块哈希的全部或者一部分
+string
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/count/search?keyword=6D5M
+返回实例
+{
" data " : 26 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+查询到的区块总数
+
+
+
+3.5 根据哈希查询区块
+ GET /ledgers/{ledger}/blocks/search?keyword={keyword}&fromIndex={start_index}&count={count}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+所要搜索的账本,需要完整的账本哈希
+string
+
+
+query
+keyword
+是
+区块哈希的全部或者一部分
+string
+
+
+query
+start_index
+否
+查询区块结果起始序号,默认为0
+string
+
+
+query
+count
+否
+查询区块结果返回数量,默认最大值为100,小于0或大于100均返回最大可返回结果集
+string
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/search?keyword=6D5M&fromIndex=0&count=-1
+
+返回实例
+{
" data " : {
" blocks " : [
{
" hash " : " 6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb "
}
]
} ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+blocks
+查询到的区块列表
+
+
+hash
+区块哈希值
+
+
+height
+区块高度
+
+
+txCount
+区块内交易数量
+
+
+
+4 交易
+4.1 获取账本交易总数
+GET /ledgers/{ledger}/txs/count
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/count
+返回实例
+{
" data " : 688 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+交易数量
+
+
+
+4.2 根据区块高度查询区块内的交易数量
+GET /ledgers/{ledger}/blocks/height/{block_height}/txs/additional-count
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+path
+block_height
+是
+区块高度
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/height/66/txs/additional-count
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb/txs/additional-count
+返回实例
+{
" data " : 86 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+交易数量
+
+
+
+4.3 根据区块哈希查询区块内的交易数量
+GET /ledgers/{ledger}/blocks/hash/{block_hash}/txs/additional-count
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+path
+block_hash
+是
+区块哈希
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb/txs/additional-count
+返回实例
+{
" data " : 86 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+交易数量
+
+
+
+4.4 获取指定高度的区块交易列表
+GET /ledgers/{ledger}/blocks/height/{height}/txs?fromIndex={start_index}&count={count}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+path
+height
+是
+区块高度
+数字
+
+
+query
+start_index
+否
+查询交易的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回交易的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/height/66/txs?fromIndex=0&count=-1
+返回实例
+{
" data " : [
{
" blockHeight " : 1 ,
" executionState " : " SUCCESS " ,
" transactionContent " : {
" ledgerHash " : {
" value " : " 657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs "
} ,
" operations " : [
{
" userID " : {
" address " : {
" value " : " 5SmBgzsrnY6u9Y7DgSSkXfTkCgp83hiFin3v "
} ,
" pubKey " : {
" value " : " mb5kukaqjWtXyAerfHU1JDtVwabSeBU5c3khMZbNh7R8VJ "
}
}
} ,
{
" accountID " : {
" address " : {
" value " : " 5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha "
} ,
" pubKey " : {
" value " : " mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR "
}
}
} ,
{
" contractID " : {
" address " : {
" value " : " 5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha "
} ,
" pubKey " : {
" value " : " mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR "
}
} ,
" chainCode " : " ---------- "
} ,
{
" contractAddress " : {
" value " : " mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR "
} ,
" event " : " ---------- " ,
" args " : " ---------- "
} ,
{
" writeSet " : [ {
" key " : " jdchain " ,
" value " : {
" type " : " TEXT " ,
" value " : {
" value " : " ---------- "
}
} ,
" expectedVersion " : 0
} ] ,
" accountAddress " : {
" value " : " mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR "
}
}
] ,
" hash " : {
" value " : " 6BLtM1agb7ERKoN5AJgZKiTjzdS7BpjgzQNYK8ZeDqotA "
}
} ,
" endpointSignatures " : [
{
" digest " : {
" value " : " 42pbfM5YKnf39Gitr4UsjTCzhhnJjwNyi8MnLFYgP4VKewTLzHitzArHEMrCt3hZYUe5ex9XvqtmiCoWpeAbdc31F "
} ,
" pubKey " : {
" value " : " mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS "
}
}
] ,
" nodeSignatures " : [
{
" digest " : {
" value " : " 66SQ95SbDaApAJhN2NsFx5sfAQTxsWhMW26D5iPqXc1jZU9rJEhRnqT1nzt62ZAcCvsfrjEsay3MxqXYA5tWPoA2U "
} ,
" pubKey " : {
" value " : " mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS "
}
}
]
}
] ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+executionState
+交易执行结果
+
+
+transactionContent.hash
+交易的哈希
+
+
+transactionContent.operations
+交易的操作列表
+
+
+endpointSignatures
+终端签名列表
+
+
+nodeSignatures
+节点的签名列表
+
+
+
+4.5 获取指定哈希的区块的交易列表
+GET /ledgers/{ledger}/blocks/hash/{block_hash}/txs?fromIndex={start_index}&count={count}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+path
+block_hash
+是
+区块哈希
+字符串
+
+
+query
+start_index
+否
+查询交易的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回交易的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb/txs?fromIndex=0&count=-1
+返回实例
+参考
+4.6 获取交易详细信息
+GET /ledgers/{ledger}/txs/hash/{tx_hash}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+path
+tx_hash
+是
+交易哈希
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/hash/6BLtM1agb7ERKoN5AJgZKiTjzdS7BpjgzQNYK8ZeDqotA
+返回实例
+{
" data " : {
" blockHeight " : 1 ,
" executionState " : " SUCCESS " ,
" transactionContent " : {
" ledgerHash " : {
" value " : " 657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs "
} ,
" operations " : [
{
" userID " : {
" address " : {
" value " : " 5SmBgzsrnY6u9Y7DgSSkXfTkCgp83hiFin3v "
} ,
" pubKey " : {
" value " : " mb5kukaqjWtXyAerfHU1JDtVwabSeBU5c3khMZbNh7R8VJ "
}
}
} ,
{
" accountID " : {
" address " : {
" value " : " 5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha "
} ,
" pubKey " : {
" value " : " mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR "
}
}
} ,
{
" contractID " : {
" address " : {
" value " : " 5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha "
} ,
" pubKey " : {
" value " : " mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR "
}
} ,
" chainCode " : " ---------- "
} ,
{
" contractAddress " : {
" value " : " mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR "
} ,
" event " : " ---------- " ,
" args " : " ---------- "
} ,
{
" writeSet " : [ {
" key " : " jdchain " ,
" value " : {
" type " : " TEXT " ,
" value " : {
" value " : " ---------- "
}
} ,
" expectedVersion " : 0
} ] ,
" accountAddress " : {
" value " : " mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR "
}
}
] ,
" hash " : {
" value " : " 6BLtM1agb7ERKoN5AJgZKiTjzdS7BpjgzQNYK8ZeDqotA "
}
} ,
" endpointSignatures " : [
{
" digest " : {
" value " : " 42pbfM5YKnf39Gitr4UsjTCzhhnJjwNyi8MnLFYgP4VKewTLzHitzArHEMrCt3hZYUe5ex9XvqtmiCoWpeAbdc31F "
} ,
" pubKey " : {
" value " : " mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS "
}
}
] ,
" nodeSignatures " : [
{
" digest " : {
" value " : " 66SQ95SbDaApAJhN2NsFx5sfAQTxsWhMW26D5iPqXc1jZU9rJEhRnqT1nzt62ZAcCvsfrjEsay3MxqXYA5tWPoA2U "
} ,
" pubKey " : {
" value " : " mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS "
}
}
]
} ,
" success " : true
}
+说明
+参考
+4.7 根据哈希查询交易总数
+ GET /ledgers/{ledgers}/txs/count/search?keyword={keyword}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledgers
+是
+所要搜索的账本范围,需要完整的账本哈希
+string
+
+
+query
+keyword
+是
+交易哈希,签名者公钥,或者节点公钥的全部或者部分的
+string
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/search?keyword=6BLt
+返回实例
+{
" data " : 36 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+指定交易数量
+
+
+
+4.8 根据哈希查询交易
+ GET /ledgers/{ledgers}/txs/search?keyword={keyword}&fromIndex={start_index}&count={count}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledgers
+是
+所要搜索的账本范围,需要完整的账本哈希
+string
+
+
+query
+keyword
+是
+交易哈希,签名者公钥,或者节点公钥的全部或者部分的
+string
+
+
+query
+start_index
+否
+查询交易的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回交易的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/search?keyword=6BLt
+
+返回
+{
" data " : {
" txs " : [
{
" hash " : " 6L3ehswCmC1jqBfvGJP9vaPx8qxkLsieu2aRgYepmkiw3 "
}
]
} ,
" success " : true
}
+5 用户
+5.1 获取用户总数
+GET /ledgers/{ledger}/users/count
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/count
+返回实例
+{
" data " : 4 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+用户总数
+
+
+
+5.2 获取用户列表
+GET /ledgers/{ledger}/users?fromIndex={start_index}&count={count}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+query
+start_index
+否
+查询用户的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回用户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users?fromIndex=0&count=-1
+返回实例
+{
" data " : [ {
" address " : {
" value " : " 5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522 "
} ,
" pubKey " : {
" value " : " mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS "
} ,
" rootHash " : {
" value " : " 5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522 "
}
} ] ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+address.value
+用户地址
+
+
+pubKey.value
+用户公钥
+
+
+
+5.3 获取用户详细信息
+GET /ledgers/{ledger}/users/address/{address}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+path
+address
+是
+用户地址
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/address/55SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522
+返回实例
+{
" data " : {
" address " : {
" value " : " 5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522 "
} ,
" pubKey " : {
" value " : " mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS "
} ,
" rootHash " : {
" value " : " 5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522 "
}
} ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+address.value
+用户地址
+
+
+pubKey.value
+用户公钥
+
+
+rootHash.value
+用户根Hash
+
+
+
+5.4 用户查询数量
+ GET /ledgers/{ledger}/users/count/search?keyword={keyword}
+说明
+用户有公钥和地址两个属性,可以通过公钥或者地址查找特定用户数量,也可以返回全部用户的数量
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+所要搜索的账本,需要完整的账本哈希
+string
+
+
+query
+keyword
+是
+用户的公钥或者地址的全部或者部分
+string
+
+
+query
+start_index
+否
+查询用户的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回用户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/count/search?keyword=5Sm
+返回实例
+{
" data " : 4 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+用户数量
+
+
+
+5.5 用户查询
+ GET /ledgers/{ledger}/users/search?keyword={keyword}&fromIndex={start_index}&count={count}
+说明
+用户有公钥和地址两个属性,可以通过公钥或者地址查找特定用户,也可以返回全部用户的列表
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+所要搜索的账本,需要完整的账本哈希
+string
+
+
+query
+keyword
+是
+用户的公钥或者地址的全部或者部分
+string
+
+
+query
+start_index
+否
+查询用户的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回用户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/search?keyword=5Sm&fromIndex=0&count=-1
+
+返回实例
+{
" data " : {
" users " : [
{
" address " : {
" value " : " 5SmAGKgmXyj5VsVvJgHbYCJ67iTizwSkNpw1 "
} ,
" pubKey " : {
" value " : " mb97eG4bba2EjrgjXYiD9chAstjg4HaNuV5xgCtSHc5TeB "
} ,
" rootHash " : {
" value " : " 5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522 "
}
}
]
} ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+address.value
+用户地址
+
+
+pubKey.value
+用户公钥
+
+
+rootHash.value
+用户根Hash
+
+
+
+6 数据账户
+6.1 获取账户列表
+GET /ledgers/{ledger}/accounts?fromIndex={start_index}&count={count}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+query
+start_index
+否
+查询数据账户的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回数据账户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts?fromIndex=0&count=-1
+返回实例
+{
" data " : [ {
" address " : {
" value " : " 5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa "
} ,
" rootHash " : {
" value " : " 6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM "
} ,
" pubKey " : {
" value " : " mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs "
}
} ] ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+address.value
+账户地址
+
+
+pubKey.value
+账户公钥
+
+
+rootHash.value
+默克尔树根哈希
+
+
+
+6.2 获取账户详细信息
+GET /ledgers/{ledger}/accounts/address/{address}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+path
+address
+是
+账户地址
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa
+返回实例
+{
" data " : {
" address " : {
" value " : " 5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa "
} ,
" rootHash " : {
" value " : " 6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM "
} ,
" pubKey " : {
" value " : " mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs "
}
} ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+address.value
+账户地址
+
+
+pubKey.value
+账户公钥
+
+
+rootHash.value
+默克尔树根哈希
+
+
+
+6.3 获取账户总数
+GET /ledgers/{ledger}/accounts/count
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/count
+返回实例
+{
" data " : 18 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+账户数量
+
+
+
+6.4 查询数据账户匹配的数量
+GET /ledgers/{ledger}/accounts/count/search?keyword={keyword}
+说明
+通过账户的公钥和地址的全部或者部分查询特定账户的总数量,也可以通过KV值的Key来查询含有该Key的账户的总数量
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+所要搜索的账本,需要完整的账本哈希
+字符串
+
+
+query
+keyword
+是
+数据账户的公钥或者地址的全部或者部分,或者是KV值的key
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/count/search?keyword=jd
+返回实例
+{
" data " : 2 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+账户数量
+
+
+
+6.5 查询数据账户
+ GET /ledgers/{ledger}/accounts/search?keyword={keyword}&fromIndex={start_index}&count={count}
+说明
+通过账户的公钥和地址的全部或者部分查询特定账户,也可以通过KV值的Key来查询含有该Key的账户
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+所要搜索的账本,需要完整的账本哈希
+string
+
+
+query
+keyword
+是
+数据账户的公钥或者地址的全部或者部分,或者是KV值的key
+string
+
+
+query
+start_index
+否
+查询数据账户的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回数据账户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/search?keyword=5Sm5V&fromIndex=0&count=-1
+
+返回实例
+{
" data " : {
" accounts " : [
{
" address " : {
" value " : " 5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa "
} ,
" rootHash " : {
" value " : " 6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM "
} ,
" pubKey " : {
" value " : " mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs "
}
}
]
} ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+address.value
+账户地址
+
+
+pubKey.value
+账户公钥
+
+
+rootHash.value
+数据账户根Hash
+
+
+
+6.6 获取某数据账户KV总数
+ GET /ledgers/{ledger}/accounts/address/{address}/entries/count
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+path
+address
+是
+账户地址
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/entries/count
+返回实例
+{
" data " : 66 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+KV总数
+
+
+
+6.7 获取某数据账户KV详情
+ GET/POST /ledgers/{ledger}/accounts/address/{address}/entries?fromIndex={start_index}&count={count}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+path
+address
+是
+账户地址
+字符串
+
+
+form
+keys
+是
+key详细内容列表
+字符串
+
+
+query
+start_index
+否
+查询数据账户对应KV的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回数据账户对应KV的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+
+keys说明:
+ 1)keys使用表单方式提交,且keys为需要查询Key的列表,列表中每个Key都需要为完整Key
+ 2)Key提交方式使用GET或POST均可
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/entries
+说明:表单提交参数为keys={"jd", "jdchain"}
+返回实例
+{
" data " : [
{
" key " : " jd " ,
" version " : 0 ,
" type " : " TEXT " ,
" value " : " www.jd.com "
} ,
{
" key " : " jdchain " ,
" version " : 0 ,
" type " : " TEXT " ,
" value " : " www.blockchain.com "
} ] ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+key
+键
+
+
+version
+版本号
+
+
+type
+value类型
+
+
+value
+值
+
+
+
+6.8 查询某数据账户键数量
+ GET /ledgers/{ledger}/accounts/address/{address}/keys/count/search?keyword={keyword}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+所要搜索的账本,需要完整的账本哈希
+string
+
+
+path
+address
+是
+所要搜索的数据账户地址,需要完整的数据账户地址
+string
+
+
+query
+keyword
+否
+键的部分字符,空表示全部
+string
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/keys/count/search?keyword=j
+返回实例
+{
" data " : 66 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+条件查询键总数
+
+
+
+6.9 查询某数据账户键
+ GET /ledgers/{ledger}/accounts/address/{address}/keys/search?keyword={keyword}&fromIndex={start_index}&count={count}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+所要搜索的账本,需要完整的账本哈希
+string
+
+
+path
+address
+是
+所要搜索的数据账户地址,需要完整的数据账户地址
+string
+
+
+query
+keyword
+否
+键的部分字符,空表示全部
+string
+
+
+query
+start_index
+否
+查询数据账户对应Key的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回数据账户对应Key的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/keys/search?keyword=j&fromIndex=0&count=-1
+返回实例
+{
" data " : [
{
" key " : " jd "
} ,
{
" key " : " jdchain "
} ] ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+key
+键
+
+
+
+7 搜索
+7.1 搜索区块链
+ GET /ledgers/{ledger}/all/search?keyword={keyword}&fromIndex={start_index}&count={count}
+说明
+通过关键字搜索区块数据,支持区块哈希,交易哈希,用户公钥和地址,合约公钥和地址,数据账户哈希和地址的搜索
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+所要搜索的账本,需要完整的账本哈希
+string
+
+
+query
+keyword
+是
+关键字
+string
+
+
+query
+start_index
+否
+查询匹配结果的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询匹配结果的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/all/search?keyword=5Sm5V&fromIndex=0&count=-1
+返回实例
+{
" message " : " OK " ,
" code " : 0 ,
" data " : {
" blocks " : . . . ,
" txs " : . . . ,
" users " : . . . ,
" accounts " : . . . ,
" contracts " : . . . ,
} ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+blocks
+参考
+
+
+txs
+参考
+
+
+users
+参考
+
+
+accounts
+参考
+
+
+contracts
+参考
+
+
+
+8 合约
+8.1 获取合约列表
+GET /ledgers/{ledger}/contracts?fromIndex={start_index}&count={count}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+query
+start_index
+否
+查询合约的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回合约的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts?fromIndex=0&count=-1
+返回实例
+{
" data " : [ {
" address " : {
" value " : " 5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa "
} ,
" rootHash " : {
" value " : " 6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM "
} ,
" pubKey " : {
" value " : " mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs "
}
} ] ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+address.value
+账户地址
+
+
+pubKey.value
+账户公钥
+
+
+rootHash.value
+默克尔树根哈希
+
+
+
+8.2 获取合约详细信息
+GET /ledgers/{ledger}/contracts/address/{address}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+path
+address
+是
+合约地址
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa
+返回实例
+{
" data " : {
" address " : {
" value " : " 5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa "
} ,
" rootHash " : {
" value " : " 6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM "
} ,
" pubKey " : {
" value " : " mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs "
}
} ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+address.value
+账户地址
+
+
+pubKey.value
+账户公钥
+
+
+rootHash.value
+默克尔树根哈希
+
+
+
+8.3 获取合约总数
+GET /ledgers/{ledger}/contracts/count
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+账本哈希
+字符串
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/count
+返回实例
+{
" data " : 27 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+合约数量
+
+
+
+8.4 查询指定合约数量
+GET /ledgers/{ledger}/contracts/count/search?keyword={keyword}
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+所要搜索的账本范围,需要完整的账本哈希
+string
+
+
+query
+keyword
+是
+合约的公钥或者地址的全部或者一部分
+string
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/count/search?keyword=5Sm2
+返回实例
+{
" data " : 2 ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+data
+合约数量
+
+
+
+8.5 合约查询
+ GET /ledgers/{ledger}/contracts/search?keyword={keyword}&fromIndex={start_index}&count={count}
+说明
+合约有公钥和地址两个属性,可以通过合约的这两个属性查询特定合约,也可以返回一个当前所有合约的列表
+参数
+
+
+
+请求类型
+名称
+是否必需
+说明
+数据类型
+
+
+
+
+path
+ledger
+是
+所要搜索的账本范围,需要完整的账本哈希
+string
+
+
+query
+keyword
+是
+合约的公钥或者地址的全部或者一部分
+string
+
+
+query
+start_index
+否
+查询合约的起始序号,默认为0
+数字
+
+
+query
+count
+否
+查询返回合约的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集
+数字
+
+
+
+请求实例
+http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/earch?keyword=5Sm2&fromIndex=0&count=-1
+
+返回
+{
" data " : {
" contracts " : [
{
" address " : {
" value " : " 5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa "
} ,
" rootHash " : {
" value " : " 6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM "
} ,
" pubKey " : {
" value " : " mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs "
}
}
]
} ,
" success " : true
}
+说明
+
+
+
+名称
+说明
+
+
+
+
+address.value
+合约地址
+
+
+pubKey.value
+合约公钥
+
+
+rootHash
+合约根Hash
+
+
+
+
diff --git a/source/deployment/deployment-gateway/src/main/resources/scripts/shutdown.sh b/source/deployment/deployment-gateway/src/main/resources/scripts/shutdown.sh
new file mode 100644
index 00000000..7418f49a
--- /dev/null
+++ b/source/deployment/deployment-gateway/src/main/resources/scripts/shutdown.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+#启动Home路径
+BOOT_HOME=$(cd `dirname $0`;cd ../; pwd)
+
+#进程启动后PID.log所在路径
+PID_LOG=$BOOT_HOME/bin/PID.log
+
+#从启动文件中读取PID
+if [ -f "$PID_LOG" ]; then
+ # File exist
+ echo "Read PID From File:[$PID_LOG] ..."
+ PID_LINE=`sed -n '$p' $PID_LOG`
+ echo "Last Gateway Boot Info = $PID_LINE ..."
+ if [[ $PID_LINE == *GW_BOOT_PID* ]]; then
+ LOG_PID=$(echo $PID_LINE | cut -d "=" -f 2 | cut -d "[" -f 2 | cut -d "]" -f 1)
+ echo "Last Gateway Boot PID = $LOG_PID ..."
+ PID=`ps -ef | grep deployment-gateway | grep $LOG_PID | grep -v grep | awk '{print $2}'`
+ fi
+#启动文件不存在则直接通过PS进行过滤
+else
+ PID=`ps -ef | grep $BOOT_HOME/lib/deployment-gateway | grep -v grep | awk '{print $2}'`
+fi
+
+#通过Kill命令将进程杀死
+if [ -z "$PID" ]; then
+ echo "Unable to find gateway PID. stop aborted."
+else
+ echo "Start to kill PID = $PID ..."
+ kill -9 $PID
+ echo "Gateway has been stopped ..."
+fi
\ No newline at end of file
diff --git a/source/deployment/deployment-gateway/src/main/resources/scripts/startup.sh b/source/deployment/deployment-gateway/src/main/resources/scripts/startup.sh
new file mode 100644
index 00000000..eecb9daf
--- /dev/null
+++ b/source/deployment/deployment-gateway/src/main/resources/scripts/startup.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+HOME=$(cd `dirname $0`;cd ../; pwd)
+GATEWAY=$(ls $HOME/lib | grep deployment-gateway-)
+if [ ! -n "$GATEWAY" ]; then
+ echo "GateWay Is Null !!!"
+else
+ nohup java -jar -server $HOME/lib/$GATEWAY -c $HOME/config/gateway.conf -sp $HOME/config/application.properties $* > gw.out 2>&1 &
+fi
\ No newline at end of file
diff --git a/source/deployment/deployment-peer/pom.xml b/source/deployment/deployment-peer/pom.xml
new file mode 100644
index 00000000..fb4f6a79
--- /dev/null
+++ b/source/deployment/deployment-peer/pom.xml
@@ -0,0 +1,80 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ deployment
+ 0.8.2.RELEASE
+
+ deployment-peer
+
+
+
+ com.jd.blockchain
+ peer
+ ${project.version}
+
+
+ com.jd.blockchain
+ runtime-modular
+ ${project.version}
+
+
+ com.jd.blockchain
+ runtime-modular-booter
+ ${project.version}
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ com.jd.blockchain.boot.peer.PeerBooter
+ true
+ .
+ false
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ make-assembly
+ package
+
+ single
+
+
+ jdchain-peer
+
+ src/main/resources/assembly.xml
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/deployment/deployment-peer/src/main/java/com/jd/blockchain/boot/peer/PeerBooter.java b/source/deployment/deployment-peer/src/main/java/com/jd/blockchain/boot/peer/PeerBooter.java
new file mode 100644
index 00000000..d04ada1a
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/java/com/jd/blockchain/boot/peer/PeerBooter.java
@@ -0,0 +1,78 @@
+package com.jd.blockchain.boot.peer;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.jd.blockchain.runtime.boot.HomeBooter;
+import com.jd.blockchain.runtime.boot.HomeContext;
+
+/**
+ * Peer starter;
+ *
+ * @author huanghaiquan
+ *
+ */
+public class PeerBooter {
+
+ public static final String MODULAR_FACTORY_CLASS = "com.jd.blockchain.runtime.modular.ModularFactory";
+
+ public static final String MODULAR_FACTORY_METHOD = "startSystem";
+
+ public static final Class>[] MODULAR_FACTORY_METHOD_ARG_TYPES = { String.class, boolean.class, ClassLoader.class,
+ String.class, ClassLoader.class, String[].class };
+
+ public static final String SYSTEM_MAIN_CLASS = "com.jd.blockchain.peer.PeerServerBooter";
+
+ public static void main(String[] args) {
+ try {
+
+ HomeContext homeContext = HomeBooter.createHomeContext(args);
+
+ startPeer(homeContext);
+
+ writePID(homeContext.getHomeDir());
+ } catch (Exception e) {
+ System.err.println("Error!!! --[" + e.getClass().getName() + "] " + e.getMessage());
+ }
+ }
+
+ public static void startPeer(HomeContext home) throws IOException, ClassNotFoundException, NoSuchMethodException,
+ SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+
+ // start system;
+ Class> modularFactoryClass = home.getSystemClassLoader().loadClass(MODULAR_FACTORY_CLASS);
+ Method modularFactoryMethod = modularFactoryClass.getMethod(MODULAR_FACTORY_METHOD,
+ MODULAR_FACTORY_METHOD_ARG_TYPES);
+
+ Object[] systemStartingArgs = { home.getRuntimeDir(), home.isProductMode(), home.getLibsClassLoader(),
+ SYSTEM_MAIN_CLASS, home.getSystemClassLoader(), home.getStartingArgs() };
+ modularFactoryMethod.invoke(null, systemStartingArgs);
+ }
+
+ private static final void writePID(String homeDir) throws IOException {
+ String pidFilePath = homeDir + File.separator + "bin" + File.separator + "PID.log";
+ File pidFile = new File(pidFilePath);
+ if (!pidFile.exists()) {
+ pidFile.createNewFile();
+ }
+ String name = ManagementFactory.getRuntimeMXBean().getName();
+ String pid = name.split("@")[0];
+ List bootInfos = new ArrayList<>();
+ bootInfos.add("JDChain peer node starts to boot ......\r\n");
+ bootInfos.add(String.format("PEER_BOOT_TIME = [%s] \r\n", new Date().toString()));
+ bootInfos.add(String.format("PEER_BOOT_PID = [%s] \r\n", pid));
+ try (FileOutputStream outputStream = new FileOutputStream(pidFile)) {
+ for (String bootInfo : bootInfos) {
+ outputStream.write(bootInfo.getBytes());
+ }
+ outputStream.flush();
+ }
+ }
+}
diff --git a/source/deployment/deployment-peer/src/main/resources/assembly.xml b/source/deployment/deployment-peer/src/main/resources/assembly.xml
new file mode 100644
index 00000000..d445b9cb
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/assembly.xml
@@ -0,0 +1,82 @@
+
+
+ ${project.version}
+
+ zip
+
+ false
+
+
+ src/main/resources/scripts
+ bin
+
+
+ src/main/resources/config
+ config
+
+
+ src/main/resources/docs
+ docs
+
+
+
+
+ false
+ true
+ system
+
+ com.jd.blockchain:ledger-core
+ com.jd.blockchain:storage-service
+ com.jd.blockchain:storage-rocksdb
+ com.jd.blockchain:storage-redis
+ com.jd.blockchain:storage-composite
+ com.jd.blockchain:runtime-modular
+ com.jd.blockchain:runtime-modular-booter
+ com.jd.blockchain:peer
+ com.jd.blockchain:deployment-peer
+
+
+
+ false
+ true
+ libs
+
+ com.jd.blockchain:ledger-core
+ com.jd.blockchain:storage-service
+ com.jd.blockchain:storage-rocksdb
+ com.jd.blockchain:storage-redis
+ com.jd.blockchain:storage-composite
+ com.jd.blockchain:runtime-modular
+ com.jd.blockchain:runtime-modular-booter
+ com.jd.blockchain:peer
+ com.jd.blockchain:deployment-peer
+
+
+
+
+
+
+ true
+
+ com.jd.blockchain:tools-initializer-booter
+
+
+ libs
+ false
+
+
+
+ true
+
+ com.jd.blockchain:tools-keygen-booter
+
+
+ libs
+ false
+
+
+
+
\ No newline at end of file
diff --git a/source/deployment/deployment-peer/src/main/resources/config/init/bftsmart.config b/source/deployment/deployment-peer/src/main/resources/config/init/bftsmart.config
new file mode 100644
index 00000000..99bcff8d
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/config/init/bftsmart.config
@@ -0,0 +1,172 @@
+
+# Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+############################################
+###### Consensus Commit Block Parameters: transaction count ######
+############################################
+system.block.txsize=15
+
+############################################
+###### Consensus Commit Block Parameters: delay time ######
+############################################
+system.block.maxdelay=500
+
+############################################
+###### #Consensus Participant0 ######
+############################################
+
+system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
+system.server.0.network.host=127.0.0.1
+system.server.0.network.port=16000
+system.server.0.network.secure=false
+
+############################################
+###### #Consensus Participant1 ######
+############################################
+
+system.server.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ
+system.server.1.network.host=127.0.0.1
+system.server.1.network.port=16010
+system.server.1.network.secure=false
+
+############################################
+###### #Consensus Participant2 ######
+############################################
+
+system.server.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R
+system.server.2.network.host=127.0.0.1
+system.server.2.network.port=16020
+system.server.2.network.secure=false
+
+############################################
+###### #Consensus Participant3 ######
+############################################
+
+system.server.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR
+system.server.3.network.host=127.0.0.1
+system.server.3.network.port=16030
+system.server.3.network.secure=false
+
+
+############################################
+####### Communication Configurations #######
+############################################
+
+#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value)
+#This parameter is not currently being used
+#system.authentication.hmacAlgorithm = HmacSHA1
+
+#Specify if the communication system should use a thread to send data (true or false)
+system.communication.useSenderThread = true
+
+#Force all processes to use the same public/private keys pair and secret key. This is useful when deploying experiments
+#and benchmarks, but must not be used in production systems.
+system.communication.defaultkeys = true
+
+############################################
+### Replication Algorithm Configurations ###
+############################################
+
+#Number of servers in the group
+system.servers.num = 4
+
+#Maximum number of faulty replicas
+system.servers.f = 1
+
+#Timeout to asking for a client request
+system.totalordermulticast.timeout = 2000
+
+
+#Maximum batch size (in number of messages)
+system.totalordermulticast.maxbatchsize = 400
+
+#Number of nonces (for non-determinism actions) generated
+system.totalordermulticast.nonces = 10
+
+#if verification of leader-generated timestamps are increasing
+#it can only be used on systems in which the network clocks
+#are synchronized
+system.totalordermulticast.verifyTimestamps = false
+
+#Quantity of messages that can be stored in the receive queue of the communication system
+system.communication.inQueueSize = 500000
+
+# Quantity of messages that can be stored in the send queue of each replica
+system.communication.outQueueSize = 500000
+
+#Set to 1 if SMaRt should use signatures, set to 0 if otherwise
+system.communication.useSignatures = 0
+
+#Set to 1 if SMaRt should use MAC's, set to 0 if otherwise
+system.communication.useMACs = 1
+
+#Set to 1 if SMaRt should use the standard output to display debug messages, set to 0 if otherwise
+system.debug = 0
+
+#Print information about the replica when it is shutdown
+system.shutdownhook = true
+
+############################################
+###### State Transfer Configurations #######
+############################################
+
+#Activate the state transfer protocol ('true' to activate, 'false' to de-activate)
+system.totalordermulticast.state_transfer = true
+
+#Maximum ahead-of-time message not discarded
+system.totalordermulticast.highMark = 10000
+
+#Maximum ahead-of-time message not discarded when the replica is still on EID 0 (after which the state transfer is triggered)
+system.totalordermulticast.revival_highMark = 10
+
+#Number of ahead-of-time messages necessary to trigger the state transfer after a request timeout occurs
+system.totalordermulticast.timeout_highMark = 200
+
+############################################
+###### Log and Checkpoint Configurations ###
+############################################
+
+system.totalordermulticast.log = true
+system.totalordermulticast.log_parallel = false
+system.totalordermulticast.log_to_disk = false
+system.totalordermulticast.sync_log = false
+
+#Period at which BFT-SMaRt requests the state to the application (for the state transfer state protocol)
+system.totalordermulticast.checkpoint_period = 1000
+system.totalordermulticast.global_checkpoint_period = 120000
+
+system.totalordermulticast.checkpoint_to_disk = false
+system.totalordermulticast.sync_ckp = false
+
+
+############################################
+###### Reconfiguration Configurations ######
+############################################
+
+#Replicas ID for the initial view, separated by a comma.
+# The number of replicas in this parameter should be equal to that specified in 'system.servers.num'
+system.initial.view = 0,1,2,3
+
+#The ID of the trust third party (TTP)
+system.ttp.id = 7002
+
+#This sets if the system will function in Byzantine or crash-only mode. Set to "true" to support Byzantine faults
+system.bft = true
+
+#Custom View Storage;
+#view.storage.handler=bftsmart.reconfiguration.views.DefaultViewStorage
+
+
diff --git a/source/deployment/deployment-peer/src/main/resources/config/init/ledger-init.conf b/source/deployment/deployment-peer/src/main/resources/config/init/ledger-init.conf
new file mode 100644
index 00000000..38b1788a
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/config/init/ledger-init.conf
@@ -0,0 +1,66 @@
+#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取;
+ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe
+
+#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途;
+#ledger.name=
+
+#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置;
+cons_parti.count=4
+
+
+#第0个参与方的名称
+cons_parti.0.name=xx-0.com
+#第0个参与方的公钥文件路径
+cons_parti.0.pubkey-path=
+#第0个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
+cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
+#第0个参与方的账本初始服务的主机
+cons_parti.0.initializer.host=127.0.0.1
+#第0个参与方的账本初始服务的端口
+cons_parti.0.initializer.port=17000
+#第0个参与方的账本初始服务是否开启安全连接
+cons_parti.0.initializer.secure=false
+
+
+#第1个参与方的名称
+cons_parti.1.name=xx-1.com
+#第1个参与方的公钥文件路径
+cons_parti.1.pubkey-path=
+#第1个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
+cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ
+#第1个参与方的账本初始服务的主机
+cons_parti.1.initializer.host=127.0.0.1
+#第1个参与方的账本初始服务的端口
+cons_parti.1.initializer.port=17010
+#第1个参与方的账本初始服务是否开启安全连接
+cons_parti.1.initializer.secure=false
+
+
+#第2个参与方的名称
+cons_parti.2.name=xx-2.com
+#第2个参与方的公钥文件路径
+cons_parti.2.pubkey-path=
+#第2个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
+cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R
+#第2个参与方的账本初始服务的主机
+cons_parti.2.initializer.host=127.0.0.1
+#第2个参与方的账本初始服务的端口
+cons_parti.2.initializer.port=17020
+#第2个参与方的账本初始服务是否开启安全连接
+cons_parti.2.initializer.secure=false
+
+
+#第3个参与方的名称
+cons_parti.3.name=xx-3.com
+#第3个参与方的公钥文件路径
+cons_parti.3.pubkey-path=
+#第3个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
+cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR
+#第3个参与方的账本初始服务的主机
+cons_parti.3.initializer.host=127.0.0.1
+#第3个参与方的账本初始服务的端口
+cons_parti.3.initializer.port=17030
+#第3个参与方的账本初始服务是否开启安全连接
+cons_parti.3.initializer.secure=false
+
+
diff --git a/source/deployment/deployment-peer/src/main/resources/config/init/local.conf b/source/deployment/deployment-peer/src/main/resources/config/init/local.conf
new file mode 100644
index 00000000..0a749a04
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/config/init/local.conf
@@ -0,0 +1,32 @@
+#当前参与方的 id
+local.parti.id=0
+
+#当前参与方的公钥
+local.parti.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
+
+#当前参与方的私钥(密文编码)
+local.parti.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY
+
+#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入
+local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY
+
+#账本初始化完成后生成的"账本绑定配置文件"的输出目录
+#推荐使用绝对路径,相对路径以当前文件(local.conf)所在目录为基准
+ledger.binding.out=../..
+
+#账本数据库的连接字符
+#rocksdb数据库连接格式:rocksdb://{path}
+#redis数据库连接格式:redis://{ip}:{prot}/{db}
+ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db
+
+#账本数据库的连接口令
+ledger.db.pwd=
+
+#共识配置文件路径
+#推荐使用绝对路径,相对路径以当前文件(local.conf)所在目录为基准
+consensus.conf=bftsmart.config
+
+#共识Providers配置
+#BftSmart共识Provider:com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider
+#简单消息共识Provider:com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider
+consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider
diff --git a/source/deployment/deployment-peer/src/main/resources/config/init/mq/ledger-init.conf b/source/deployment/deployment-peer/src/main/resources/config/init/mq/ledger-init.conf
new file mode 100644
index 00000000..38b1788a
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/config/init/mq/ledger-init.conf
@@ -0,0 +1,66 @@
+#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取;
+ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe
+
+#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途;
+#ledger.name=
+
+#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置;
+cons_parti.count=4
+
+
+#第0个参与方的名称
+cons_parti.0.name=xx-0.com
+#第0个参与方的公钥文件路径
+cons_parti.0.pubkey-path=
+#第0个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
+cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
+#第0个参与方的账本初始服务的主机
+cons_parti.0.initializer.host=127.0.0.1
+#第0个参与方的账本初始服务的端口
+cons_parti.0.initializer.port=17000
+#第0个参与方的账本初始服务是否开启安全连接
+cons_parti.0.initializer.secure=false
+
+
+#第1个参与方的名称
+cons_parti.1.name=xx-1.com
+#第1个参与方的公钥文件路径
+cons_parti.1.pubkey-path=
+#第1个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
+cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ
+#第1个参与方的账本初始服务的主机
+cons_parti.1.initializer.host=127.0.0.1
+#第1个参与方的账本初始服务的端口
+cons_parti.1.initializer.port=17010
+#第1个参与方的账本初始服务是否开启安全连接
+cons_parti.1.initializer.secure=false
+
+
+#第2个参与方的名称
+cons_parti.2.name=xx-2.com
+#第2个参与方的公钥文件路径
+cons_parti.2.pubkey-path=
+#第2个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
+cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R
+#第2个参与方的账本初始服务的主机
+cons_parti.2.initializer.host=127.0.0.1
+#第2个参与方的账本初始服务的端口
+cons_parti.2.initializer.port=17020
+#第2个参与方的账本初始服务是否开启安全连接
+cons_parti.2.initializer.secure=false
+
+
+#第3个参与方的名称
+cons_parti.3.name=xx-3.com
+#第3个参与方的公钥文件路径
+cons_parti.3.pubkey-path=
+#第3个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
+cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR
+#第3个参与方的账本初始服务的主机
+cons_parti.3.initializer.host=127.0.0.1
+#第3个参与方的账本初始服务的端口
+cons_parti.3.initializer.port=17030
+#第3个参与方的账本初始服务是否开启安全连接
+cons_parti.3.initializer.secure=false
+
+
diff --git a/source/deployment/deployment-peer/src/main/resources/config/init/mq/local.conf b/source/deployment/deployment-peer/src/main/resources/config/init/mq/local.conf
new file mode 100644
index 00000000..08e99b84
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/config/init/mq/local.conf
@@ -0,0 +1,32 @@
+#当前参与方的 id
+local.parti.id=0
+
+#当前参与方的公钥
+local.parti.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
+
+#当前参与方的私钥(密文编码)
+local.parti.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY
+
+#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入
+local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY
+
+#账本初始化完成后生成的"账本绑定配置文件"的输出目录
+#推荐使用绝对路径,相对路径以当前文件(local.conf)所在目录为基准
+ledger.binding.out=../
+
+#账本数据库的连接字符
+#rocksdb数据库连接格式:rocksdb://{path}
+#redis数据库连接格式:redis://{ip}:{prot}/{db}
+ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db
+
+#账本数据库的连接口令
+ledger.db.pwd=
+
+#共识配置文件路径
+#推荐使用绝对路径,相对路径以当前文件(local.conf)所在目录为基准
+consensus.conf=mq.config
+
+#共识Providers配置
+#BftSmart共识Provider:com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider
+#简单消息共识Provider:com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider
+consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider
diff --git a/source/deployment/deployment-peer/src/main/resources/config/init/mq/mq.config b/source/deployment/deployment-peer/src/main/resources/config/init/mq/mq.config
new file mode 100644
index 00000000..424a2e2b
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/config/init/mq/mq.config
@@ -0,0 +1,26 @@
+# MQ连接地址,格式:{MQ类型}://{IP}:{PORT}
+system.msg.queue.server=rabbit://127.0.0.1:5672
+
+# 当前账本交易发送队列主题(不同账本需不同主题)
+system.msg.queue.topic.tx=tx-topic
+
+# 当前账本结块消息应答队列主题
+system.msg.queue.topic.bl=bl-topic
+
+# 当前账本普通消息主题
+system.msg.queue.topic.msg=msg-topic
+
+# 当前账本结块最大交易数
+system.msg.queue.block.txsize=1000
+
+# 当前账本结块最大时长(单位:毫秒)
+system.msg.queue.block.maxdelay=2000
+
+# 当前账本节点总数
+system.servers.num=4
+
+# 当前账本对应节点的公钥信息列表
+system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
+system.server.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ
+system.server.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R
+system.server.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR
diff --git a/source/deployment/deployment-peer/src/main/resources/config/ledger-binding.conf b/source/deployment/deployment-peer/src/main/resources/config/ledger-binding.conf
new file mode 100644
index 00000000..6c3cd9e5
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/config/ledger-binding.conf
@@ -0,0 +1,19 @@
+#绑定的账本的hash列表;以逗号分隔;
+ledger.bindings=6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i
+
+
+#第 1 个账本[6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i]的配置;
+#账本的当前共识参与方的ID;
+binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.parti.id=0
+#账本的当前共识参与方的私钥文件的保存路径;
+binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.parti.pk-path=
+#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性;
+binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.parti.pk=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY
+#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入;
+binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY
+#账本的当前共识参与方地址
+binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.parti.address=5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522
+#账本的存储数据库的连接字符串;
+binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.db.uri=rocksdb:///export/Data/rocksdb/rocksdb0.db
+#账本的存储数据库的连接口令;
+binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.db.pwd=
diff --git a/source/deployment/deployment-peer/src/main/resources/docs/imgs/browser.jpeg b/source/deployment/deployment-peer/src/main/resources/docs/imgs/browser.jpeg
new file mode 100644
index 00000000..2cfb38a7
Binary files /dev/null and b/source/deployment/deployment-peer/src/main/resources/docs/imgs/browser.jpeg differ
diff --git a/source/deployment/deployment-peer/src/main/resources/docs/imgs/keys.jpeg b/source/deployment/deployment-peer/src/main/resources/docs/imgs/keys.jpeg
new file mode 100644
index 00000000..9cdfea53
Binary files /dev/null and b/source/deployment/deployment-peer/src/main/resources/docs/imgs/keys.jpeg differ
diff --git a/source/deployment/deployment-peer/src/main/resources/docs/imgs/structure.png b/source/deployment/deployment-peer/src/main/resources/docs/imgs/structure.png
new file mode 100644
index 00000000..bbac0652
Binary files /dev/null and b/source/deployment/deployment-peer/src/main/resources/docs/imgs/structure.png differ
diff --git a/source/deployment/deployment-peer/src/main/resources/docs/安装部署.MD b/source/deployment/deployment-peer/src/main/resources/docs/安装部署.MD
new file mode 100644
index 00000000..28ea020d
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/docs/安装部署.MD
@@ -0,0 +1,317 @@
+# JDChain安装部署指南
+
+## 1. 部署环境
+### 1.1 系统部署结构
+![部署结构](imgs/structure.png)
+
+JDChain有三类节点:Peer、网关和客户端。peer节点是区块链主节点,参与共识、账本操作等;网关节点(GateWay)与Peer节点通信,负责区块链浏览器及消息传递;客户端与网关通信,可以进行写入或查询账本等操作。
+
+JDChain默认采用Rocksdb作为存储数据库,每个Peer节点与一个(或多个)Rocksdb实例连接,Rocksdb实例的数量与账本相关,不同Peer节点对应不同的Rocksdb实例。
+
+JDChain也支持Redis作为其存储数据库,Redis数据库不必和Peer节点安装在同一服务器,只需要配置正确即可(后面配置会说到)。
+
+JDChain默认共识机制采用的是BFTSmart,该共识算法要求最低四个共识节点,BFTSmart 对作恶节点的支持为:N = 3f+1,即在N个节点共识中可允许f个作恶节点,当节点数增加时可根据该公式进行配置。
+
+ *本安装部署指南以四个节点为例进行说明。*
+
+
+-----
+
+### 1.2 服务器配置
+
+#### 1.2.1 硬件配置
+为了更好的运行,JDChain推荐使用 **Linux** 操作系统,Peer节点自身运行对内存要求较低,但对于Rocksdb存储而言,其对运行环境有较高要求,以下为单Peer节点可正常运行 **亿级交易** 的参考指标:
+ + 内核2.7.x及以上;
+ + CPU主频 ≥ 2.0GHz;
+ + 内存 ≥ 64G;
+ + 可用硬盘容量 >= 200G
+
+
+#### 1.2.2 软件配置
+
+JDChain使用Java开发,其软件需求如下:
+ + JDK:Version ≥ 1.8。
+
+
+> 注意:若数据库使用的Redis,则需要对应数据库服务器安装Redis,Redis版本要求>=3.x。
+
+-----
+
+## 2. 系统安装与配置
+### 2.1 安装包结构
+JDChain默认发布的安装包有两个,一个是Peer节点打包程序,一个是Gateway节点打包程序,两者默认都是用tar.gz(或zip)打包的。
+
+#### 2.1.1 Peer节点安装包
+Peer节点打包程序为:jdchain-peer-$version.tar.gz或jdchain-peer-$version.zip,若是tar.gz压缩文件可通过tar命令解压(如下):
+```shell
+tar -xzvf jdchain-peer-$version.tar.gz
+```
+若是zip文件可通过unzip命令解压(如下):
+```shell
+unzip jdchain-peer-$version.zip
+```
+
+> 注意:$version为JDChain发布的版本号
+
+Peer打包程序解压完后的安装包结构如下:
+
+ + bin
+ - keygen.sh
+ - ledger-init.sh
+ - startup.sh
+ - shutdown.sh
+ + config
+ - init
+ + ledger-init.conf
+ + local.conf
+ + bftsmart.config(默认)
+ - keys
+ + %.priv
+ + %.pub
+ + %.pwd
+ - ledger-binding.conf
+ - application.properties
+ + docs
+ + libs
+ + system
+
+其中目录说明如下:
+ + **bin** :相关命令操作目录;
+ + **config** :对应命令的配置目录,keys路径默认可能不存在,会在执行相关脚本时自动创建;
+ + **docs** :相关文档保存目录;
+ + **libs** :项目运行依赖第三方及非system依赖包保存路径;
+ + **system** :项目运行系统包保存路径;
+
+> 1)注意: *bin* 目录下所有的文件都需要可执行权限,在Linux环境下可简单参考如下命令:
+```
+cd bin
+chmod 777 *
+```
+> 2)注意:system文件夹包括运行时系统主要加载的jar文件,为系统安全运行定制,开发者无须关心此项。
+
+#### 2.1.2 Gateway节点安装包
+Gateway节点打包程序为:jdchain-gateway-$version.tar.gz或jdchain-gateway-$version.zip,其解压完后的安装包结构如下:
+
+ + bin
+ - startup.sh
+ - shutdown.sh
+ + config
+ - gateway.conf
+ + lib
+
+
+ 其中目录说明如下:
+ + **bin** :相关命令操作目录;
+ + **config** :对应命令的配置目录;
+ + **lib** :gateway运行时所需jar包路径;
+
+### 2.2 注册用户
+JDChain以一个 *密钥对* 标识一个用户,任何区块链操作都必须有用户的签名信息,因此首先需要创建一个用户。
+JDChain提供了创建用户的工具,可直接通过命令行生成一个新用户。切换到bin路径,执行命令:
+```shell
+./keygen.sh -n jd-com
+```
+其中jd-com是生成的密钥对文件前缀,需自定义。
+执行命令过程中需要输入密钥对中 *私钥* 加密的密码,并选择是否将该密码编码保存(推荐选择保存,该密码编码会保存至%.pwd文件中)。
+> 注意:请妥善保存该密码,切勿丢失!
+
+执行命令后,该 *密钥对* 文件会生成到 *config/keys* 路径,如下所示:
+
+![keys](imgs/keys.jpeg)
+
+其中,jd-com.priv是签名私钥的密文形式内容(以口令哈希作为对称密钥,对签名私钥进行对称加密后的密文数据),jd-com.pub为签名公钥内容,jd-com.pwd是用于加密签名私钥的口令哈希值。
+> 注意:%.pwd记录的编码在后续Gateway配置时会用到!
+
+**请将所有Peer节点都按照上述流程注册一个独立的用户,在实际生产环境中该过程可能由不同的参与方完成。**
+
+### 2.3 账本初始化
+账本初始化是所有参与Peer节点进行共识,并生成初始化账本至数据库的过程。该过程需要所有节点共同参与,同时启动。
+
+#### 2.3.1 初始化配置
+*config/init* 目录下有三个配置文件需要修改:**local.conf** 、 **ledger-init.conf** 和 **bftsmart.config** 。
+ + local.conf描述账本初始化的本地(即当前节点)配置;
+ + ledger-init.conf描述账本初始化过程中涉及到的其他参与Peer节点配置信息;
+ + bftsmart.config为BFTSmart进行共识的相关配置。
+
+##### 2.3.1.1 local.conf配置
+```config
+#当前参与方的 id
+local.parti.id=0
+
+#当前参与方的公钥
+local.parti.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
+
+#当前参与方的私钥(密文编码)
+local.parti.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY
+
+#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入
+local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY
+
+#账本初始化完成后生成的"账本绑定配置文件"的输出目录
+ledger.binding.out=../config
+
+#账本数据库的连接字符,下为rocksdb样例
+#rocksdb数据库连接格式:rocksdb://{path}
+#redis数据库连接格式:redis://{ip}:{prot}/{db}
+ledger.db.uri=rocksdb:///export/app/peer/rocks.db/rocksdb0.db
+
+#账本数据库的连接口令
+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* 文件中的内容;其他参数根据实际环境进行配置。
+
+
+##### 2.3.1.2 ledger-init.conf配置
+```config
+#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取;
+ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe
+
+#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途;
+#ledger.name=
+
+#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置;
+cons_parti.count=4
+
+
+#第0个参与方的名称
+cons_parti.0.name=parti-0.com
+#第0个参与方的公钥文件路径
+cons_parti.0.pubkey-path=
+#第0个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
+cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
+#第0个参与方的账本初始服务的主机
+cons_parti.0.initializer.host=127.0.0.1
+#第0个参与方的账本初始服务的端口
+cons_parti.0.initializer.port=17000
+#第0个参与方的账本初始服务是否开启安全连接
+cons_parti.0.initializer.secure=false
+
+
+#第1个参与方的名称
+cons_parti.1.name=parti-1.com
+#第1个参与方的公钥文件路径
+cons_parti.1.pubkey-path=
+#第1个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
+cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ
+#第1个参与方的账本初始服务的主机
+cons_parti.1.initializer.host=127.0.0.1
+#第1个参与方的账本初始服务的端口
+cons_parti.1.initializer.port=17010
+#第1个参与方的账本初始服务是否开启安全连接
+cons_parti.1.initializer.secure=false
+
+```
+账本初始化过程中,需要其他Peer节点的公钥信息进行验证及保存,因此每个节点都需要获取其他Peer节点的公钥信息至本地。配置中cons_parti.N.*****中的N需按照实际每个参与方定义的序号配置(该配置为local.conf中的local.parti.id)。其他参数根据实际环境进行配置。
+
+> 注意:上述config中只显示了两个参与方(Peer节点)的配置信息,其他参与方(默认是4个节点)的配置方式一致!
+
+##### 2.3.1.1 bftsmart.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.port=16000
+
+system.servers.num = 4
+
+system.servers.f = 1
+
+system.initial.view = 0,1,2,3
+```
+> 注意:上述只是将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.port:第n个节点的共识端口;
+ + system.servers.num:共识节点总数;
+ + system.servers.f:共识节点最大支持的作恶节点数;
+ + system.initial.view:为每个共识节点分配的viewID,每个viewID之间使用英文逗号分隔,viewID不允许重复,且viewID总数需要和system.servers.num一致。
+
+#### 2.3.2 初始化部署
+按照 *2.3.1章节* 配置完成后,只需要启动ledger-init.sh即可,可参考如下命令:
+```shell
+cd bin
+./ledger-init.sh
+```
+执行命令之后,会在 *config* 目录下生成ledger-binding.conf文件,该文件即账本初始化生成的文件,Peer节点启动时需要依赖该文件。
+> 1)注意:因为JDChain支持多账本形式,若config/ledger-binding.conf文件在初始化之前就存在的话,初始化操作后不会覆盖其中的内容,会以追加的方式写入。若第一次创建账本,建议先将该文件删除再进行初始化!
+
+> 2)注意:Peer节点会定时检测ledger-binding.conf,有新账本加入时会自动进行更新,不需要重启Peer节点!
+
+账本初始化成功后,每个Peer节点对应的Rocksdb都会写入该账本相关的数据。
+
+### 2.4 Peer节点安装
+Peer节点启动依赖于 *config* 目录下ledger-binding.conf的配置,该文件由 *2.3章节* 操作生成,无须做任何修改;application.properties文件中主要用于配置Peer节点对外HTTP端口。
+> 注意:application.properties文件可能不存在,可手动创建。该文件中配置适用于SpringBoot2.x版本的相关参数,不限于目前内置的启动端口。
+
+由于Peer节点启动后会自动与其他参与节点进行通信,因此需要同时启动4个Peer节点,只需要执行startup.sh即可,参考命令:
+```shell
+cd bin
+./startup.sh
+```
+> 1)注意:startup.sh命令中可修改启动端口,默认为:-p 17080;
+
+> 2)注意:Peer节点会与账本中涉及到的参与方进行通信,当通信不成功(例如有节点尚未启动)时,会自动进行重试,因此多个Peer节点启动可不必完全同时进行。
+
+### 2.5 Gateway节点安装
+GateWay(网关)节点可以认为是一个过滤节点,交易的提交及账本的查询都需要通过网关节点与Peer节点进行通信。
+Gateway程序可独立部署,不需要依赖Peer节点,它的操作环境是 *2.1.2章节* 中解压后的环境,网关节点启动依赖于配置文件 *config/gateway.conf*。
+
+```config
+#网关的HTTP服务地址;
+http.host=127.0.0.1
+#网关的HTTP服务端口;
+http.port=8081
+#网关的HTTP服务上下文路径,可选;
+#http.context-path=
+
+#共识节点的服务地址;
+peer.host=127.0.0.1
+#共识节点的服务端口;
+peer.port=7080
+#共识节点的服务是否启用安全证书;
+peer.secure=false
+#共识节点的服务提供解析器
+peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider
+
+#数据检索服务对应URL
+#若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示
+data.retrieval.url=http://192.168.151.39:10001
+
+#默认公钥的内容(Base58编码数据);
+keys.default.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
+#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一;
+keys.default.privkey-path=
+#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一;
+keys.default.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY
+#默认私钥的解码密码;
+keys.default.privkey-password=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY
+```
+其中keys.default.%配置是网关的角色配置,目前暂不支持网关自定义角色,因此,网关可选择4个参与方的任何一个作为其配置(主要是密钥对)。
+>注意:keys.default.privkey-password填写内容为*2.2章节*中生成的%.pwd文件中的内容!
+
+启动网关节点只需要执行:startup.sh即可,参考命令:
+```shell
+cd bin
+./startup.sh
+```
+
+网关节点启动成功后,可通过访问区块链浏览器获取其账本相关信息:http://[ip]:[port]
+下图为区块链浏览器首页,可供参考:
+
+![区块链浏览器](imgs/browser.jpeg)
+
+>注意:JDChain网关节点默认自带区块链浏览器!!!
diff --git a/source/deployment/deployment-peer/src/main/resources/docs/安装部署.html b/source/deployment/deployment-peer/src/main/resources/docs/安装部署.html
new file mode 100644
index 00000000..1d75f3c9
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/docs/安装部署.html
@@ -0,0 +1,714 @@
+
+
+
+
+ 安装部署
+
+
+ JDChain安装部署指南
+1. 部署环境
+1.1 系统部署结构
+
+JDChain有三类节点:Peer、网关和客户端。peer节点是区块链主节点,参与共识、账本操作等;网关节点(GateWay)与Peer节点通信,负责区块链浏览器及消息传递;客户端与网关通信,可以进行写入或查询账本等操作。
+JDChain默认采用Rocksdb作为存储数据库,每个Peer节点与一个(或多个)Rocksdb实例连接,Rocksdb实例的数量与账本相关,不同Peer节点对应不同的Rocksdb实例。
+JDChain也支持Redis作为其存储数据库,Redis数据库不必和Peer节点安装在同一服务器,只需要配置正确即可(后面配置会说到)。
+JDChain默认共识机制采用的是BFTSmart,该共识算法要求最低四个共识节点,BFTSmart 对作恶节点的支持为:N = 3f+1,即在N个节点共识中可允许f个作恶节点,当节点数增加时可根据该公式进行配置。
+ 本安装部署指南以四个节点为例进行说明。
+
+1.2 服务器配置
+1.2.1 硬件配置
+为了更好的运行,JDChain推荐使用 Linux 操作系统,Peer节点自身运行对内存要求较低,但对于Rocksdb存储而言,其对运行环境有较高要求,以下为单Peer节点可正常运行 亿级交易 的参考指标:
+
+内核2.7.x及以上;
+CPU主频 ≥ 2.0GHz;
+内存 ≥ 64G;
+可用硬盘容量 >= 200G
+
+1.2.2 软件配置
+JDChain使用Java开发,其软件需求如下:
+
+
+ 注意:若数据库使用的Redis,则需要对应数据库服务器安装Redis,Redis版本要求>=3.x。
+
+
+2. 系统安装与配置
+2.1 安装包结构
+JDChain默认发布的安装包有两个,一个是Peer节点打包程序,一个是Gateway节点打包程序,两者默认都是用tar.gz(或zip)打包的。
+2.1.1 Peer节点安装包
+Peer节点打包程序为:jdchain-peer-$version.tar.gz或jdchain-peer-$version.zip,若是tar.gz压缩文件可通过tar命令解压(如下):
+tar -xzvf jdchain-peer- $ version .tar.gz
+若是zip文件可通过unzip命令解压(如下):
+unzip jdchain-peer- $ version .zip
+
+注意:$version为JDChain发布的版本号
+
+Peer打包程序解压完后的安装包结构如下:
+
+bin
+keygen.sh
+ledger-init.sh
+startup.sh
+shutdown.sh
+
+
+config
+init
+ledger-init.conf
+local.conf
+bftsmart.config(默认)
+
+
+keys
+
+ledger-binding.conf
+application.properties
+
+
+docs
+libs
+system
+
+其中目录说明如下:
+
+bin :相关命令操作目录;
+config :对应命令的配置目录,keys路径默认可能不存在,会在执行相关脚本时自动创建;
+docs :相关文档保存目录;
+libs :项目运行依赖第三方及非system依赖包保存路径;
+system :项目运行系统包保存路径;
+
+
+1)注意: bin 目录下所有的文件都需要可执行权限,在Linux环境下可简单参考如下命令:
+cd bin
chmod 777 *
2)注意:system文件夹包括运行时系统主要加载的jar文件,为系统安全运行定制,开发者无须关心此项。
+
+2.1.2 Gateway节点安装包
+Gateway节点打包程序为:jdchain-gateway-$version.tar.gz或jdchain-gateway-$version.zip,其解压完后的安装包结构如下:
+
+bin
+startup.sh
+shutdown.sh
+
+
+config
+
+lib
+
+ 其中目录说明如下:
+
+bin :相关命令操作目录;
+config :对应命令的配置目录;
+lib :gateway运行时所需jar包路径;
+
+2.2 注册用户
+JDChain以一个 密钥对 标识一个用户,任何区块链操作都必须有用户的签名信息,因此首先需要创建一个用户。 JDChain提供了创建用户的工具,可直接通过命令行生成一个新用户。切换到bin路径,执行命令:
+./keygen.sh -n jd-com
+其中jd-com是生成的密钥对文件前缀,需自定义。 执行命令过程中需要输入密钥对中 私钥 加密的密码,并选择是否将该密码编码保存(推荐选择保存,该密码编码会保存至%.pwd文件中)。
+
+注意:请妥善保存该密码,切勿丢失!
+
+执行命令后,该 密钥对 文件会生成到 config/keys 路径,如下所示:
+
+其中,jd-com.priv是签名私钥的密文形式内容(以口令哈希作为对称密钥,对签名私钥进行对称加密后的密文数据),jd-com.pub为签名公钥内容,jd-com.pwd是用于加密签名私钥的口令哈希值。
+
+注意:%.pwd记录的编码在后续Gateway配置时会用到!
+
+请将所有Peer节点都按照上述流程注册一个独立的用户,在实际生产环境中该过程可能由不同的参与方完成。
+2.3 账本初始化
+账本初始化是所有参与Peer节点进行共识,并生成初始化账本至数据库的过程。该过程需要所有节点共同参与,同时启动。
+2.3.1 初始化配置
+config/init 目录下有三个配置文件需要修改:local.conf 、 ledger-init.conf 和 bftsmart.config 。
+
+local.conf描述账本初始化的本地(即当前节点)配置;
+ledger-init.conf描述账本初始化过程中涉及到的其他参与Peer节点配置信息;
+bftsmart.config为BFTSmart进行共识的相关配置。
+
+2.3.1.1 local.conf配置
+#当前参与方的 id
local.parti.id=0
#当前参与方的公钥
local.parti.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
#当前参与方的私钥(密文编码)
local.parti.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY
#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入
local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY
#账本初始化完成后生成的"账本绑定配置文件"的输出目录
ledger.binding.out=../config
#账本数据库的连接字符,下为rocksdb样例
#rocksdb数据库连接格式:rocksdb://{path}
#redis数据库连接格式:redis://{ip}:{prot}/{db}
ledger.db.uri=rocksdb:///export/app/peer/rocks.db/rocksdb0.db
#账本数据库的连接口令
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 文件中的内容;其他参数根据实际环境进行配置。
+2.3.1.2 ledger-init.conf配置
+#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取;
ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe
#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途;
#ledger.name=
#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置;
cons_parti.count=4
#第0个参与方的名称
cons_parti.0.name=parti-0.com
#第0个参与方的公钥文件路径
cons_parti.0.pubkey-path=
#第0个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
#第0个参与方的账本初始服务的主机
cons_parti.0.initializer.host=127.0.0.1
#第0个参与方的账本初始服务的端口
cons_parti.0.initializer.port=17000
#第0个参与方的账本初始服务是否开启安全连接
cons_parti.0.initializer.secure=false
#第1个参与方的名称
cons_parti.1.name=parti-1.com
#第1个参与方的公钥文件路径
cons_parti.1.pubkey-path=
#第1个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ
#第1个参与方的账本初始服务的主机
cons_parti.1.initializer.host=127.0.0.1
#第1个参与方的账本初始服务的端口
cons_parti.1.initializer.port=17010
#第1个参与方的账本初始服务是否开启安全连接
cons_parti.1.initializer.secure=false
+账本初始化过程中,需要其他Peer节点的公钥信息进行验证及保存,因此每个节点都需要获取其他Peer节点的公钥信息至本地。配置中cons_parti.N.* 中的N需按照实际每个参与方定义的序号配置(该配置为local.conf中的local.parti.id)。其他参数根据实际环境进行配置。
+
+注意:上述config中只显示了两个参与方(Peer节点)的配置信息,其他参与方(默认是4个节点)的配置方式一致!
+
+2.3.1.1 bftsmart.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.port=16000
system.servers.num = 4
system.servers.f = 1
system.initial.view = 0,1,2,3
+
+注意:上述只是将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.port:第n个节点的共识端口;
+system.servers.num:共识节点总数;
+system.servers.f:共识节点最大支持的作恶节点数;
+system.initial.view:为每个共识节点分配的viewID,每个viewID之间使用英文逗号分隔,viewID不允许重复,且viewID总数需要和system.servers.num一致。
+
+2.3.2 初始化部署
+按照 2.3.1章节 配置完成后,只需要启动ledger-init.sh即可,可参考如下命令:
+cd bin
./ledger-init.sh
+执行命令之后,会在 config 目录下生成ledger-binding.conf文件,该文件即账本初始化生成的文件,Peer节点启动时需要依赖该文件。
+
+1)注意:因为JDChain支持多账本形式,若config/ledger-binding.conf文件在初始化之前就存在的话,初始化操作后不会覆盖其中的内容,会以追加的方式写入。若第一次创建账本,建议先将该文件删除再进行初始化!
+2)注意:Peer节点会定时检测ledger-binding.conf,有新账本加入时会自动进行更新,不需要重启Peer节点!
+
+账本初始化成功后,每个Peer节点对应的Rocksdb都会写入该账本相关的数据。
+2.4 Peer节点安装
+Peer节点启动依赖于 config 目录下ledger-binding.conf的配置,该文件由 2.3章节 操作生成,无须做任何修改;application.properties文件中主要用于配置Peer节点对外HTTP端口。
+
+注意:application.properties文件可能不存在,可手动创建。该文件中配置适用于SpringBoot2.x版本的相关参数,不限于目前内置的启动端口。
+
+由于Peer节点启动后会自动与其他参与节点进行通信,因此需要同时启动4个Peer节点,只需要执行startup.sh即可,参考命令:
+cd bin
./startup.sh
+
+1)注意:startup.sh命令中可修改启动端口,默认为:-p 17080;
+2)注意:Peer节点会与账本中涉及到的参与方进行通信,当通信不成功(例如有节点尚未启动)时,会自动进行重试,因此多个Peer节点启动可不必完全同时进行。
+
+2.5 Gateway节点安装
+GateWay(网关)节点可以认为是一个过滤节点,交易的提交及账本的查询都需要通过网关节点与Peer节点进行通信。
+Gateway程序可独立部署,不需要依赖Peer节点,它的操作环境是 2.1.2章节 中解压后的环境,网关节点启动依赖于配置文件 config/gateway.conf 。
+#网关的HTTP服务地址;
http.host=127.0.0.1
#网关的HTTP服务端口;
http.port=8081
#网关的HTTP服务上下文路径,可选;
#http.context-path=
#共识节点的服务地址;
peer.host=127.0.0.1
#共识节点的服务端口;
peer.port=7080
#共识节点的服务是否启用安全证书;
peer.secure=false
#共识节点的服务提供解析器
peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider
#数据检索服务对应URL
#若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示
data.retrieval.url= http://192.168.151.39:10001
#默认公钥的内容(Base58编码数据);
keys.default.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一;
keys.default.privkey-path=
#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一;
keys.default.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY
#默认私钥的解码密码;
keys.default.privkey-password=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY
+其中keys.default.%配置是网关的角色配置,目前暂不支持网关自定义角色,因此,网关可选择4个参与方的任何一个作为其配置(主要是密钥对)。
+
+注意:keys.default.privkey-password填写内容为2.2章节 中生成的%.pwd文件中的内容!
+
+启动网关节点只需要执行:startup.sh即可,参考命令:
+cd bin
./startup.sh
+网关节点启动成功后,可通过访问区块链浏览器获取其账本相关信息:http://[ip]:[port ]
+下图为区块链浏览器首页,可供参考:
+
+
+注意:JDChain网关节点默认自带区块链浏览器!!!
+
+
diff --git a/source/deployment/deployment-peer/src/main/resources/scripts/keygen.sh b/source/deployment/deployment-peer/src/main/resources/scripts/keygen.sh
new file mode 100644
index 00000000..0249e85d
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/scripts/keygen.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+HOME=$(cd `dirname $0`;cd ../; pwd)
+boot_file=$(ls ../libs | grep tools-keygen-booter-)
+if [ ! -n "$boot_file" ]; then
+ echo "tools-keygen-booter is null"
+else
+ if [ ! -d $HOME/config/keys ];then
+ mkdir $HOME/config/keys
+ echo "create new dir $HOME/config/keys"
+ echo "keys file will be saved $HOME/config/keys"
+ else
+ echo "keys file will be saved $HOME/config/keys"
+ fi
+ java -jar $HOME/libs/$boot_file -o $HOME/config/keys -l $HOME/config/init/local.conf $*
+fi
\ No newline at end of file
diff --git a/source/deployment/deployment-peer/src/main/resources/scripts/ledger-init.sh b/source/deployment/deployment-peer/src/main/resources/scripts/ledger-init.sh
new file mode 100644
index 00000000..e30a91c6
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/scripts/ledger-init.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+HOME=$(cd `dirname $0`;cd ../; pwd)
+boot_file=$(ls ../libs | grep tools-initializer-booter-)
+if [ ! -n "$boot_file" ]; then
+ echo "tools-initializer-booter is null"
+else
+ java -jar $HOME/libs/$boot_file -l $HOME/config/init/local.conf -i $HOME/config/init/ledger-init.conf $*
+fi
diff --git a/source/deployment/deployment-peer/src/main/resources/scripts/shutdown.sh b/source/deployment/deployment-peer/src/main/resources/scripts/shutdown.sh
new file mode 100644
index 00000000..da76d093
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/scripts/shutdown.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+#启动Home路径
+BOOT_HOME=$(cd `dirname $0`;cd ../; pwd)
+
+#进程启动后PID.log所在路径
+PID_LOG=$BOOT_HOME/bin/PID.log
+
+#从启动文件中读取PID
+if [ -f "$PID_LOG" ]; then
+ # File exist
+ echo "Read PID From File:[$PID_LOG] ..."
+ PID_LINE=`sed -n '$p' $PID_LOG`
+ echo "Last Peer Boot Info = $PID_LINE ..."
+ if [[ $PID_LINE == *PEER_BOOT_PID* ]]; then
+ LOG_PID=$(echo $PID_LINE | cut -d "=" -f 2 | cut -d "[" -f 2 | cut -d "]" -f 1)
+ echo "Last Peer Boot PID = $LOG_PID ..."
+ PID=`ps -ef | grep deployment-peer | grep $LOG_PID | grep -v grep | awk '{print $2}'`
+ fi
+#启动文件不存在则直接通过PS进行过滤
+else
+ PID=`ps -ef | grep $BOOT_HOME/system/deployment-peer | grep -v grep | awk '{print $2}'`
+fi
+
+#通过Kill命令将进程杀死
+if [ -z "$PID" ]; then
+ echo "Unable to find peer PID. stop aborted."
+else
+ echo "Start to kill PID = $PID ..."
+ kill -9 $PID
+ echo "Peer has been stopped ..."
+fi
\ No newline at end of file
diff --git a/source/deployment/deployment-peer/src/main/resources/scripts/startup.sh b/source/deployment/deployment-peer/src/main/resources/scripts/startup.sh
new file mode 100644
index 00000000..4318fdb0
--- /dev/null
+++ b/source/deployment/deployment-peer/src/main/resources/scripts/startup.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+HOME=$(cd `dirname $0`;cd ../; pwd)
+PEER=$(ls $HOME/system | grep deployment-peer-)
+if [ ! -n "$PEER" ]; then
+ echo "Peer Is Null !!!"
+else
+ nohup java -jar -server -Xmx2g -Xms2g $HOME/system/$PEER -home=$HOME -c $HOME/config/init/mq/ledger-binding.conf -p 7080 $* &
+fi
\ No newline at end of file
diff --git a/source/deployment/pom.xml b/source/deployment/pom.xml
new file mode 100644
index 00000000..4270f809
--- /dev/null
+++ b/source/deployment/pom.xml
@@ -0,0 +1,30 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ jdchain-root
+ 0.8.2.RELEASE
+
+ deployment
+ pom
+
+
+ deployment-gateway
+ deployment-peer
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/source/gateway/.gitignore b/source/gateway/.gitignore
new file mode 100644
index 00000000..24d64373
--- /dev/null
+++ b/source/gateway/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/source/gateway/pom.xml b/source/gateway/pom.xml
new file mode 100644
index 00000000..42e07bbc
--- /dev/null
+++ b/source/gateway/pom.xml
@@ -0,0 +1,119 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ jdchain-root
+ 0.8.2.RELEASE
+
+ gateway
+
+
+
+ com.jd.blockchain
+ consensus-framework
+ ${project.version}
+
+
+ com.jd.blockchain
+ consensus-mq
+ ${project.version}
+
+
+ com.jd.blockchain
+ consensus-bftsmart
+ ${project.version}
+
+
+ com.jd.blockchain
+ ledger-rpc
+ ${project.version}
+
+
+ com.jd.blockchain
+ sdk-base
+ ${project.version}
+
+
+
+ com.jd.blockchain
+ tools-keygen
+ ${project.version}
+
+
+
+ com.jd.blockchain
+ browser
+
+
+
+ com.jd.blockchain
+ utils-web
+ ${project.version}
+
+
+
+ commons-io
+ commons-io
+ ${commons-io.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ com.jd.blockchain.gateway.GatewayServerBooter
+ true
+ .
+ false
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayConfigProperties.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayConfigProperties.java
new file mode 100644
index 00000000..ba16e829
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayConfigProperties.java
@@ -0,0 +1,286 @@
+package com.jd.blockchain.gateway;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import com.jd.blockchain.utils.io.FileUtils;
+import com.jd.blockchain.utils.net.NetworkAddress;
+
+public class GatewayConfigProperties {
+ // HTTP协议相关配置项的键的前缀;
+ public static final String HTTP_PREFIX = "http.";
+ // 网关的HTTP服务地址;
+ public static final String HTTP_HOST = HTTP_PREFIX + "host";
+ // 网关的HTTP服务端口;
+ public static final String HTTP_PORT = HTTP_PREFIX + "port";
+ // 网关的HTTP服务上下文路径,可选;
+ public static final String HTTP_CONTEXT_PATH = HTTP_PREFIX + "context-path";
+
+ // 共识相关配置项的键的前缀;
+ public static final String PEER_PREFIX = "peer.";
+ // 共识节点的服务地址;
+ public static final String PEER_HOST = PEER_PREFIX + "host";
+ // 共识节点的服务端口;
+ public static final String PEER_PORT = PEER_PREFIX + "port";
+ // 共识节点的服务是否启用安全证书;
+ public static final String PEER_SECURE = PEER_PREFIX + "secure";
+ // 支持共识的Provider列表,以英文逗号分隔
+ public static final String PEER_PROVIDERS = PEER_PREFIX + "providers";
+
+ // 数据检索服务URL地址
+ public static final String DATA_RETRIEVAL_URL="data.retrieval.url";
+
+ // 密钥相关配置项的键的前缀;
+ public static final String KEYS_PREFIX = "keys.";
+ // 默认密钥相关配置项的键的前缀;
+ public static final String DEFAULT_KEYS_PREFIX = KEYS_PREFIX + "default.";
+ // 默认私钥的内容;
+ public static final String DEFAULT_PUBKEY = DEFAULT_KEYS_PREFIX + "pubkey";
+ // 默认私钥的文件存储路径;
+ public static final String DEFAULT_PRIVKEY_PATH = DEFAULT_KEYS_PREFIX + "privkey-path";
+ // 默认私钥的内容;
+ public static final String DEFAULT_PRIVKEY = DEFAULT_KEYS_PREFIX + "privkey";
+ // 默认私钥的密码;
+ public static final String DEFAULT_PK_PWD = DEFAULT_KEYS_PREFIX + "privkey-password";
+
+
+ private HttpConfig http = new HttpConfig();
+
+ private ProviderConfig providerConfig = new ProviderConfig();
+
+ private NetworkAddress masterPeerAddress;
+
+ private String dataRetrievalUrl;
+
+ private KeysConfig keys = new KeysConfig();
+
+ public HttpConfig http() {
+ return http;
+ }
+
+ public NetworkAddress masterPeerAddress() {
+ return masterPeerAddress;
+ }
+
+ public String dataRetrievalUrl() {
+ return this.dataRetrievalUrl;
+ }
+
+ public void setDataRetrievalUrl(String dataRetrievalUrl) {
+ this.dataRetrievalUrl = dataRetrievalUrl;
+ }
+
+ public ProviderConfig providerConfig() {
+ return providerConfig;
+ }
+
+ public void setMasterPeerAddress(NetworkAddress peerAddress) {
+ if (peerAddress == null) {
+ throw new IllegalArgumentException("peerAddress is null!");
+ }
+ this.masterPeerAddress = peerAddress;
+ }
+
+ public KeysConfig keys() {
+ return keys;
+ }
+
+ public GatewayConfigProperties() {
+ }
+
+ public static GatewayConfigProperties resolve(String file) {
+ Properties props = FileUtils.readProperties(file, "UTf-8");
+ return resolve(props);
+ }
+
+ public static GatewayConfigProperties resolve(File file) {
+ Properties props = FileUtils.readProperties(file, "UTf-8");
+ return resolve(props);
+ }
+
+ public static GatewayConfigProperties resolve(InputStream in) {
+ Properties props = FileUtils.readProperties(in, "UTf-8");
+ return resolve(props);
+ }
+
+ public static GatewayConfigProperties resolve(Properties props) {
+ GatewayConfigProperties configProps = new GatewayConfigProperties();
+ configProps.http.host = getProperty(props, HTTP_HOST, true);
+ configProps.http.port = getInt(props, HTTP_PORT, true);
+ configProps.http.contextPath = getProperty(props, HTTP_CONTEXT_PATH, false);
+
+ String peerHost = getProperty(props, PEER_HOST, true);
+ int peerPort = getInt(props, PEER_PORT, true);
+ boolean peerSecure = getBoolean(props, PEER_SECURE, false);
+ configProps.masterPeerAddress = new NetworkAddress(peerHost, peerPort, peerSecure);
+
+ String dataRetrievalUrl = getProperty(props, DATA_RETRIEVAL_URL, true);
+ configProps.dataRetrievalUrl = dataRetrievalUrl;
+
+ String providers = getProperty(props, PEER_PROVIDERS, true);
+ if (providers == null || providers.length() <= 0) {
+ throw new IllegalArgumentException("Miss peer providers!");
+ }
+ String[] providerArray = providers.split(",");
+ for (String provider : providerArray) {
+ configProps.providerConfig.add(provider);
+ }
+
+ configProps.keys.defaultPK.pubKeyValue = getProperty(props, DEFAULT_PUBKEY, true);
+ configProps.keys.defaultPK.privKeyPath = getProperty(props, DEFAULT_PRIVKEY_PATH, false);
+ configProps.keys.defaultPK.privKeyValue = getProperty(props, DEFAULT_PRIVKEY, false);
+ if (configProps.keys.defaultPK.privKeyPath == null && configProps.keys.defaultPK.privKeyValue == null) {
+ throw new IllegalArgumentException("Miss both of pk-path and pk content!");
+ }
+ configProps.keys.defaultPK.privKeyPassword = getProperty(props, DEFAULT_PK_PWD, true);
+
+ return configProps;
+ }
+
+ private static String getProperty(Properties props, String key, boolean required) {
+ String value = props.getProperty(key);
+ if (value != null) {
+ value = value.trim();
+ }
+ if (value == null || value.length() == 0) {
+ if (required) {
+ throw new IllegalArgumentException("Miss property[" + key + "]!");
+ }
+ return null;
+ }
+ return value;
+ }
+
+ private static boolean getBoolean(Properties props, String key, boolean required) {
+ String strBool = getProperty(props, key, required);
+ if (strBool == null) {
+ return false;
+ }
+ return Boolean.parseBoolean(strBool);
+ }
+
+ private static int getInt(Properties props, String key, boolean required) {
+ String strInt = getProperty(props, key, required);
+ if (strInt == null) {
+ return 0;
+ }
+ return getInt(strInt);
+ }
+
+ private static int getInt(String strInt) {
+ return Integer.parseInt(strInt.trim());
+ }
+
+ // ------------------------------------------------------------
+
+ public static class ProviderConfig {
+ List providers = new ArrayList<>();
+
+ public void add(String provider) {
+ providers.add(provider);
+ }
+
+ public List getProviders() {
+ return providers;
+ }
+ }
+
+ public static class HttpConfig {
+
+ private String host;
+
+ private int port;
+
+ private String contextPath;
+
+ private HttpConfig() {
+ }
+
+ private HttpConfig(String host, int port, String contextPath) {
+ this.host = host;
+ this.port = port;
+ this.contextPath = contextPath;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public String getContextPath() {
+ return contextPath;
+ }
+
+ public void setContextPath(String contextPath) {
+ this.contextPath = contextPath;
+ }
+ }
+
+ public static class KeysConfig {
+
+ private KeyPairConfig defaultPK = new KeyPairConfig();
+
+ public KeyPairConfig getDefault() {
+ return defaultPK;
+ }
+ }
+
+ public static class KeyPairConfig {
+
+ private String pubKeyValue;
+
+ private String privKeyPath;
+
+ private String privKeyValue;
+
+ private String privKeyPassword;
+
+ public String getPrivKeyPath() {
+ return privKeyPath;
+ }
+
+ public String getPrivKeyValue() {
+ return privKeyValue;
+ }
+
+ public String getPrivKeyPassword() {
+ return privKeyPassword;
+ }
+
+ public String getPubKeyValue() {
+ return pubKeyValue;
+ }
+
+ public void setPubKeyValue(String pubKeyValue) {
+ this.pubKeyValue = pubKeyValue;
+ }
+
+ public void setPrivKeyPath(String privKeyPath) {
+ this.privKeyPath = privKeyPath;
+ }
+
+ public void setPrivKeyValue(String privKeyValue) {
+ this.privKeyValue = privKeyValue;
+ }
+
+ public void setPrivKeyPassword(String privKeyPassword) {
+ this.privKeyPassword = privKeyPassword;
+ }
+
+ }
+
+}
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayConfiguration.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayConfiguration.java
new file mode 100644
index 00000000..c498d29a
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayConfiguration.java
@@ -0,0 +1,14 @@
+package com.jd.blockchain.gateway;
+
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.ComponentScan;
+
+@EnableAutoConfiguration
+@EnableConfigurationProperties
+@SpringBootApplication
+@ComponentScan
+public class GatewayConfiguration {
+}
+
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayServerBooter.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayServerBooter.java
new file mode 100644
index 00000000..34b2df7b
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayServerBooter.java
@@ -0,0 +1,150 @@
+package com.jd.blockchain.gateway;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.jd.blockchain.gateway.web.BlockBrowserController;
+import org.apache.commons.io.FileUtils;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.io.ClassPathResource;
+
+import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair;
+import com.jd.blockchain.crypto.asymmetric.PrivKey;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.tools.keygen.KeyGenCommand;
+import com.jd.blockchain.utils.ArgumentSet;
+import com.jd.blockchain.utils.BaseConstant;
+import com.jd.blockchain.utils.ConsoleUtils;
+import com.jd.blockchain.utils.ArgumentSet.ArgEntry;
+
+
+public class GatewayServerBooter {
+
+ // 当前参与方在初始化配置中的参与方列表的编号;
+ private static final String HOST_ARG = "-c";
+ //sp;针对spring.config.location这个参数进行包装;
+ private static final String SPRING_CF_LOCATION = BaseConstant.SPRING_CF_LOCATION;
+ // 是否输出调试信息;
+ private static final String DEBUG_OPT = "-debug";
+
+ public static void main(String[] args) {
+ boolean debug = false;
+ try {
+ ArgumentSet arguments = ArgumentSet.resolve(args, ArgumentSet.setting().prefix(HOST_ARG, SPRING_CF_LOCATION).option(DEBUG_OPT));
+ debug = arguments.hasOption(DEBUG_OPT);
+ ArgEntry argHost = arguments.getArg(HOST_ARG);
+ String configFile = argHost == null ? null : argHost.getValue();
+ GatewayConfigProperties configProps;
+ if (configFile == null) {
+ ConsoleUtils.info("Load build-in default configuration ...");
+ ClassPathResource configResource = new ClassPathResource("gateway.conf");
+ try (InputStream in = configResource.getInputStream()) {
+ configProps = GatewayConfigProperties.resolve(in);
+ }
+ } else {
+ ConsoleUtils.info("Load configuration ...");
+ configProps = GatewayConfigProperties.resolve(argHost.getValue());
+ }
+
+ //spring config location;
+ String springConfigLocation=null;
+ ArgumentSet.ArgEntry spConfigLocation = arguments.getArg(SPRING_CF_LOCATION);
+ if (spConfigLocation != null) {
+ springConfigLocation = spConfigLocation.getValue();
+ }else {
+ //if no the config file, then should tip as follows. but it's not a good feeling, so we create it by inputStream;
+ ConsoleUtils.info("no param:-sp, format: -sp /x/xx.properties, use the default application-gw.properties ");
+ ClassPathResource configResource = new ClassPathResource("application-gw.properties");
+ InputStream in = configResource.getInputStream();
+ File targetFile = new File(System.getProperty("user.dir")+File.separator+"conf"+File.separator+"application-gw.properties");
+ FileUtils.copyInputStreamToFile(in, targetFile);
+ springConfigLocation = "file:"+targetFile.getAbsolutePath();
+ }
+
+ // 启动服务器;
+ ConsoleUtils.info("Starting web server......");
+ GatewayServerBooter booter = new GatewayServerBooter(configProps,springConfigLocation);
+ booter.start();
+
+ ConsoleUtils.info("Peer[%s] is connected success!", configProps.masterPeerAddress().toString());
+ } catch (Exception e) {
+ ConsoleUtils.error("Error!! %s", e.getMessage());
+ if (debug) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private volatile ConfigurableApplicationContext appCtx;
+ private GatewayConfigProperties config;
+ private CryptoKeyPair defaultKeyPair;
+ private String springConfigLocation;
+ public GatewayServerBooter(GatewayConfigProperties config, String springConfigLocation) {
+ this.config = config;
+ this.springConfigLocation = springConfigLocation;
+
+ String base58Pwd = config.keys().getDefault().getPrivKeyPassword();
+ if (base58Pwd == null || base58Pwd.length() == 0) {
+ base58Pwd = KeyGenCommand.readPasswordString();
+ }
+
+ // 加载密钥;
+ PubKey pubKey = KeyGenCommand.decodePubKey(config.keys().getDefault().getPubKeyValue());
+
+ PrivKey privKey = null;
+ String base58PrivKey = config.keys().getDefault().getPrivKeyValue();
+ if (base58PrivKey == null) {
+ //注:GatewayConfigProperties 确保了 PrivKeyValue 和 PrivKeyPath 必有其一;
+ privKey = KeyGenCommand.readPrivKey(config.keys().getDefault().getPrivKeyPath(), base58Pwd);
+ } else {
+ privKey = KeyGenCommand.decodePrivKey(base58PrivKey, base58Pwd);
+ }
+ defaultKeyPair = new CryptoKeyPair(pubKey, privKey);
+ }
+
+ public synchronized void start() {
+ if (this.appCtx != null) {
+ throw new IllegalStateException("Gateway server is running already.");
+ }
+ this.appCtx = startServer(config.http().getHost(), config.http().getPort(), springConfigLocation,
+ config.http().getContextPath());
+
+ ConsoleUtils.info("\r\n\r\nStart connecting to peer ....");
+ BlockBrowserController blockBrowserController = appCtx.getBean(BlockBrowserController.class);
+ blockBrowserController.setDataRetrievalUrl(config.dataRetrievalUrl());
+ PeerConnector peerConnector = appCtx.getBean(PeerConnector.class);
+ peerConnector.connect(config.masterPeerAddress(), defaultKeyPair, config.providerConfig().getProviders());
+ ConsoleUtils.info("Peer[%s] is connected success!", config.masterPeerAddress().toString());
+ }
+
+ public synchronized void close() {
+ if (this.appCtx == null) {
+ return;
+ }
+ this.appCtx.close();
+ }
+
+ private static ConfigurableApplicationContext startServer(String host, int port, String springConfigLocation, String contextPath) {
+ List argList = new ArrayList();
+ argList.add(String.format("--server.address=%s", host));
+ argList.add(String.format("--server.port=%s", port));
+
+ if(springConfigLocation != null){
+ argList.add(String.format("--spring.config.location=%s", springConfigLocation));
+ }
+
+ if (contextPath != null) {
+ argList.add(String.format("--server.context-path=%s", contextPath));
+ }
+
+ String[] args = argList.toArray(new String[argList.size()]);
+
+ // 启动服务器;
+ ConfigurableApplicationContext appCtx = SpringApplication.run(GatewayConfiguration.class, args);
+ return appCtx;
+ }
+
+}
\ No newline at end of file
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/PeerConnector.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/PeerConnector.java
new file mode 100644
index 00000000..d25ea004
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/PeerConnector.java
@@ -0,0 +1,20 @@
+package com.jd.blockchain.gateway;
+
+import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair;
+import com.jd.blockchain.utils.net.NetworkAddress;
+
+import java.util.List;
+
+public interface PeerConnector {
+
+ NetworkAddress getPeerAddress();
+
+ boolean isConnected();
+
+ void connect(NetworkAddress peerAddress, CryptoKeyPair defaultKeyPair, List peerProviders);
+
+ void reconnect();
+
+ void close();
+
+}
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/PeerService.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/PeerService.java
new file mode 100644
index 00000000..38039582
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/PeerService.java
@@ -0,0 +1,12 @@
+package com.jd.blockchain.gateway;
+
+import com.jd.blockchain.ledger.data.TransactionService;
+import com.jd.blockchain.sdk.BlockchainQueryService;
+
+public interface PeerService {
+
+ BlockchainQueryService getQueryService();
+
+ TransactionService getTransactionService();
+
+}
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/service/DataRetrievalService.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/DataRetrievalService.java
new file mode 100644
index 00000000..d891bf83
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/DataRetrievalService.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.gateway.service.DataRetrievalService
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2019/1/15 下午3:08
+ * Description:
+ */
+package com.jd.blockchain.gateway.service;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2019/1/15
+ * @since 1.0.0
+ */
+
+public interface DataRetrievalService {
+
+ String retrieval(String url) throws Exception;
+}
\ No newline at end of file
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/service/DataRetrievalServiceHandler.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/DataRetrievalServiceHandler.java
new file mode 100644
index 00000000..d7dd0baf
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/DataRetrievalServiceHandler.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.gateway.service.DeepQueryServiceImpl
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2019/1/15 下午3:09
+ * Description:
+ */
+package com.jd.blockchain.gateway.service;
+
+import com.jd.blockchain.utils.http.agent.HttpClientPool;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2019/1/15
+ * @since 1.0.0
+ */
+@Component
+public class DataRetrievalServiceHandler implements DataRetrievalService {
+
+ @Override
+ public String retrieval(String url) throws Exception {
+ return HttpClientPool.get(url);
+ }
+}
\ No newline at end of file
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryService.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryService.java
new file mode 100644
index 00000000..4167d9f5
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryService.java
@@ -0,0 +1,27 @@
+package com.jd.blockchain.gateway.service;
+
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.ParticipantNode;
+
+/**
+ * queryService only for gateway;
+ * @Author zhaogw
+ * @Date 2019/2/22 10:37
+ */
+public interface GatewayQueryService {
+ /**
+ * get all ledgers hashs;
+ * @param fromIndex
+ * @param count
+ */
+ HashDigest[] getLedgersHash(int fromIndex, int count);
+
+ /**
+ * get the participants by range;
+ * @param ledgerHash
+ * @param fromIndex
+ * @param count
+ * @return
+ */
+ ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash, int fromIndex, int count);
+}
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryServiceImpl.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryServiceImpl.java
new file mode 100644
index 00000000..04132cec
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryServiceImpl.java
@@ -0,0 +1,37 @@
+package com.jd.blockchain.gateway.service;
+
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.gateway.PeerService;
+import com.jd.blockchain.ledger.ParticipantNode;
+import com.jd.blockchain.utils.QueryUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import java.util.Arrays;
+
+import static com.jd.blockchain.utils.BaseConstant.QUERY_LIST_MAX;
+
+/**
+ * @Author zhaogw
+ * @Date 2019/2/22 10:39
+ */
+@Component
+public class GatewayQueryServiceImpl implements GatewayQueryService {
+ @Autowired
+ private PeerService peerService;
+
+ @Override
+ public HashDigest[] getLedgersHash(int fromIndex, int count) {
+ HashDigest ledgersHash[] = peerService.getQueryService().getLedgerHashs();
+ int indexAndCount[] = QueryUtil.calFromIndexAndCount(fromIndex,count,ledgersHash.length);
+ HashDigest ledgersHashNew[] = Arrays.copyOfRange(ledgersHash,indexAndCount[0],indexAndCount[0]+indexAndCount[1]);
+ return ledgersHashNew;
+ }
+
+ @Override
+ public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash, int fromIndex, int count) {
+ ParticipantNode participantNode[] = peerService.getQueryService().getConsensusParticipants(ledgerHash);
+ int indexAndCount[] = QueryUtil.calFromIndexAndCount(fromIndex,count,participantNode.length);
+ ParticipantNode participantNodesNew[] = Arrays.copyOfRange(participantNode,indexAndCount[0],indexAndCount[0]+indexAndCount[1]);
+ return participantNodesNew;
+ }
+}
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/service/PeerConnectionManager.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/PeerConnectionManager.java
new file mode 100644
index 00000000..ef98fcc1
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/PeerConnectionManager.java
@@ -0,0 +1,108 @@
+package com.jd.blockchain.gateway.service;
+
+import javax.annotation.PreDestroy;
+
+import org.springframework.stereotype.Component;
+
+import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair;
+import com.jd.blockchain.gateway.PeerConnector;
+import com.jd.blockchain.gateway.PeerService;
+import com.jd.blockchain.ledger.data.TransactionService;
+import com.jd.blockchain.sdk.BlockchainQueryService;
+import com.jd.blockchain.sdk.service.PeerBlockchainServiceFactory;
+import com.jd.blockchain.utils.net.NetworkAddress;
+
+import java.util.List;
+
+@Component
+public class PeerConnectionManager implements PeerService, PeerConnector {
+
+ private volatile PeerBlockchainServiceFactory peerServiceFactory;
+
+ private volatile NetworkAddress peerAddress;
+
+ private volatile CryptoKeyPair gateWayKeyPair;
+
+ private volatile List peerProviders;
+
+ @Override
+ public NetworkAddress getPeerAddress() {
+ return peerAddress;
+ }
+
+ @Override
+ public boolean isConnected() {
+ return peerServiceFactory != null;
+ }
+
+ @Override
+ public synchronized void connect(NetworkAddress peerAddress, CryptoKeyPair defaultKeyPair, List peerProviders) {
+ if (isConnected()) {
+ if (this.peerAddress.equals(peerAddress)) {
+ return;
+ }
+ throw new IllegalArgumentException(
+ "This gateway has been connected to a peer, cann't be connected to another peer before closing it!");
+ }
+ setPeerAddress(peerAddress);
+ setGateWayKeyPair(defaultKeyPair);
+ setPeerProviders(peerProviders);
+ // TODO: 未实现运行时出错时动态重连;
+ peerServiceFactory = PeerBlockchainServiceFactory.connect(defaultKeyPair, peerAddress, peerProviders);
+ }
+
+ @Override
+ public synchronized void reconnect() {
+ if (!isConnected()) {
+ throw new IllegalArgumentException(
+ "This gateway has not connected to a peer, please connect it first!!!");
+ }
+ peerServiceFactory = PeerBlockchainServiceFactory.connect(gateWayKeyPair, peerAddress, peerProviders);
+ }
+
+ @Override
+ public void close() {
+ PeerBlockchainServiceFactory serviceFactory = this.peerServiceFactory;
+ if (serviceFactory != null) {
+ this.peerServiceFactory = null;
+ this.peerAddress = null;
+ serviceFactory.close();
+ }
+ }
+
+ @Override
+ public BlockchainQueryService getQueryService() {
+ PeerBlockchainServiceFactory serviceFactory = this.peerServiceFactory;
+ if (serviceFactory == null) {
+ throw new IllegalStateException("Peer connection was closed!");
+ }
+ return serviceFactory.getBlockchainService();
+ }
+
+ @Override
+ public TransactionService getTransactionService() {
+ PeerBlockchainServiceFactory serviceFactory = this.peerServiceFactory;
+ if (serviceFactory == null) {
+ throw new IllegalStateException("Peer connection was closed!");
+ }
+
+ return serviceFactory.getTransactionService();
+ }
+
+ @PreDestroy
+ private void destroy() {
+ close();
+ }
+
+ public void setPeerAddress(NetworkAddress peerAddress) {
+ this.peerAddress = peerAddress;
+ }
+
+ public void setGateWayKeyPair(CryptoKeyPair gateWayKeyPair) {
+ this.gateWayKeyPair = gateWayKeyPair;
+ }
+
+ public void setPeerProviders(List peerProviders) {
+ this.peerProviders = peerProviders;
+ }
+}
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/BlockBrowserController.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/BlockBrowserController.java
new file mode 100644
index 00000000..1c5b1fcb
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/BlockBrowserController.java
@@ -0,0 +1,519 @@
+package com.jd.blockchain.gateway.web;
+
+import com.jd.blockchain.crypto.AddressEncoding;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.gateway.PeerService;
+import com.jd.blockchain.gateway.service.DataRetrievalService;
+import com.jd.blockchain.gateway.service.GatewayQueryService;
+import com.jd.blockchain.ledger.*;
+import com.jd.blockchain.sdk.BlockchainExtendQueryService;
+import com.jd.blockchain.tools.keygen.KeyGenCommand;
+import com.jd.blockchain.utils.BaseConstant;
+import com.jd.blockchain.utils.ConsoleUtils;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+
+@RestController
+@RequestMapping(path = "/")
+public class BlockBrowserController implements BlockchainExtendQueryService {
+ private static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(BlockBrowserController.class);
+
+ @Autowired
+ private PeerService peerService;
+
+ @Autowired
+ private DataRetrievalService dataRetrievalService;
+
+ @Autowired
+ private GatewayQueryService gatewayQueryService;
+
+ private String dataRetrievalUrl;
+
+ private static final long BLOCK_MAX_DISPLAY = 3L;
+
+ private static final long GENESIS_BLOCK_HEIGHT = 0L;
+
+ @Deprecated
+// @RequestMapping(method = RequestMethod.GET, path = "ledgers")
+ @Override
+ public HashDigest[] getLedgerHashs() {
+ return peerService.getQueryService().getLedgerHashs();
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}")
+ @Override
+ public LedgerInfo getLedger(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ return peerService.getQueryService().getLedger(ledgerHash);
+ }
+
+// @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants")
+ @Override
+ public ParticipantNode[] getConsensusParticipants(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ return peerService.getQueryService().getConsensusParticipants(ledgerHash);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks")
+ public LedgerBlock[] getBlocks(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ LedgerInfo ledgerInfo = peerService.getQueryService().getLedger(ledgerHash);
+ long maxBlockHeight = ledgerInfo.getLatestBlockHeight();
+ List ledgerBlocks = new ArrayList<>();
+ for (long blockHeight = maxBlockHeight; blockHeight > GENESIS_BLOCK_HEIGHT; blockHeight--) {
+ LedgerBlock ledgerBlock = peerService.getQueryService().getBlock(ledgerHash, blockHeight);
+ ledgerBlocks.add(0, ledgerBlock);
+ if (ledgerBlocks.size() == BLOCK_MAX_DISPLAY) {
+ break;
+ }
+ }
+ // 最后增加创世区块
+ LedgerBlock genesisBlock = peerService.getQueryService().getBlock(ledgerHash, GENESIS_BLOCK_HEIGHT);
+ ledgerBlocks.add(0, genesisBlock);
+ LedgerBlock[] blocks = new LedgerBlock[ledgerBlocks.size()];
+ ledgerBlocks.toArray(blocks);
+ return blocks;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}")
+ @Override
+ public LedgerBlock getBlock(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHeight") long blockHeight) {
+ return peerService.getQueryService().getBlock(ledgerHash, blockHeight);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}")
+ @Override
+ public LedgerBlock getBlock(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHash") HashDigest blockHash) {
+ return peerService.getQueryService().getBlock(ledgerHash, blockHash);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/txs/count")
+ @Override
+ public long getTransactionCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHeight") long blockHeight) {
+ return peerService.getQueryService().getTransactionCount(ledgerHash, blockHeight);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/txs/count")
+ @Override
+ public long getTransactionCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHash") HashDigest blockHash) {
+ return peerService.getQueryService().getTransactionCount(ledgerHash, blockHash);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/txs/count")
+ @Override
+ public long getTransactionTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ return peerService.getQueryService().getTransactionTotalCount(ledgerHash);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/accounts/count")
+ @Override
+ public long getDataAccountCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHeight") long blockHeight) {
+ return peerService.getQueryService().getDataAccountCount(ledgerHash, blockHeight);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/accounts/count")
+ @Override
+ public long getDataAccountCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHash") HashDigest blockHash) {
+ return peerService.getQueryService().getDataAccountCount(ledgerHash, blockHash);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts/count")
+ @Override
+ public long getDataAccountTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ return peerService.getQueryService().getDataAccountTotalCount(ledgerHash);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/users/count")
+ @Override
+ public long getUserCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHeight") long blockHeight) {
+ return peerService.getQueryService().getUserCount(ledgerHash, blockHeight);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/users/count")
+ @Override
+ public long getUserCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHash") HashDigest blockHash) {
+ return peerService.getQueryService().getUserCount(ledgerHash, blockHash);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/users/count")
+ @Override
+ public long getUserTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ return peerService.getQueryService().getUserTotalCount(ledgerHash);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/contracts/count")
+ @Override
+ public long getContractCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHeight") long blockHeight) {
+ return peerService.getQueryService().getContractCount(ledgerHash, blockHeight);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/contracts/count")
+ @Override
+ public long getContractCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHash") HashDigest blockHash) {
+ return peerService.getQueryService().getContractCount(ledgerHash, blockHash);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts/count")
+ @Override
+ public long getContractTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ return peerService.getQueryService().getContractTotalCount(ledgerHash);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/txs")
+ @Override
+ public LedgerTransaction[] getTransactions(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHeight") long blockHeight,
+ @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex,
+ @RequestParam(name = "count", required = false, defaultValue = "-1") int count) {
+ return peerService.getQueryService().getTransactions(ledgerHash, blockHeight, fromIndex, count);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/txs")
+ @Override
+ public LedgerTransaction[] getTransactions(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHash") HashDigest blockHash,
+ @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex,
+ @RequestParam(name = "count", required = false, defaultValue = "-1") int count) {
+ return peerService.getQueryService().getTransactions(ledgerHash, blockHash, fromIndex, count);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/txs/hash/{contentHash}")
+ @Override
+ public LedgerTransaction getTransactionByContentHash(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "contentHash") HashDigest contentHash) {
+ return peerService.getQueryService().getTransactionByContentHash(ledgerHash, contentHash);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/txs/state/{contentHash}")
+ @Override
+ public TransactionState getTransactionStateByContentHash(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "contentHash") HashDigest contentHash) {
+ return peerService.getQueryService().getTransactionStateByContentHash(ledgerHash, contentHash);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/users/address/{address}")
+ @Override
+ public UserInfo getUser(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "address") String address) {
+ return peerService.getQueryService().getUser(ledgerHash, address);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts/address/{address}")
+ @Override
+ public AccountHeader getDataAccount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "address") String address) {
+
+ return peerService.getQueryService().getDataAccount(ledgerHash, address);
+ }
+
+ @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}, path = "ledgers/{ledgerHash}/accounts/{address}/entries")
+ @Override
+ public KVDataEntry[] getDataEntries(@PathVariable("ledgerHash") HashDigest ledgerHash,
+ @PathVariable("address") String address,
+ @RequestParam("keys") String... keys) {
+ return peerService.getQueryService().getDataEntries(ledgerHash, address, keys);
+ }
+
+ @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}, path = "ledgers/{ledgerHash}/accounts/address/{address}/entries")
+ @Override
+ public KVDataEntry[] getDataEntries(@PathVariable("ledgerHash") HashDigest ledgerHash,
+ @PathVariable("address") String address,
+ @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex,
+ @RequestParam(name = "count", required = false, defaultValue = "-1") int count) {
+ return peerService.getQueryService().getDataEntries(ledgerHash, address, fromIndex, count);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts/address/{address}/entries/count")
+ @Override
+ public long getDataEntriesTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "address") String address) {
+ return peerService.getQueryService().getDataEntriesTotalCount(ledgerHash, address);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts/address/{address}")
+ @Override
+ public AccountHeader getContract(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "address") String address) {
+ return peerService.getQueryService().getContract(ledgerHash, address);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/latest")
+ @Override
+ public LedgerBlock getLatestBlock(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ long latestBlockHeight = peerService.getQueryService().getLedger(ledgerHash).getLatestBlockHeight();
+ return peerService.getQueryService().getBlock(ledgerHash, latestBlockHeight);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/txs/additional-count")
+ @Override
+ public long getAdditionalTransactionCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHeight") long blockHeight) {
+ // 获取某个区块的交易总数
+ long currentBlockTxCount = peerService.getQueryService().getTransactionCount(ledgerHash, blockHeight);
+ if (blockHeight == GENESIS_BLOCK_HEIGHT) {
+ return currentBlockTxCount;
+ }
+ long lastBlockHeight = blockHeight - 1;
+ long lastBlockTxCount = peerService.getQueryService().getTransactionCount(ledgerHash, lastBlockHeight);
+ // 当前区块交易数减上个区块交易数
+ return currentBlockTxCount - lastBlockTxCount;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/txs/additional-count")
+ @Override
+ public long getAdditionalTransactionCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHash") HashDigest blockHash) {
+ LedgerBlock currentBlock = peerService.getQueryService().getBlock(ledgerHash, blockHash);
+ long currentBlockTxCount = peerService.getQueryService().getTransactionCount(ledgerHash, blockHash);
+ if (currentBlock.getHeight() == GENESIS_BLOCK_HEIGHT) {
+ return currentBlockTxCount;
+ }
+ HashDigest previousHash = currentBlock.getPreviousHash();
+ long lastBlockTxCount = peerService.getQueryService().getTransactionCount(ledgerHash, previousHash);
+ // 当前区块交易数减上个区块交易数
+ return currentBlockTxCount - lastBlockTxCount;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/txs/additional-count")
+ @Override
+ public long getAdditionalTransactionCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ LedgerInfo ledgerInfo = peerService.getQueryService().getLedger(ledgerHash);
+ long maxBlockHeight = ledgerInfo.getLatestBlockHeight();
+ long totalCount = peerService.getQueryService().getTransactionTotalCount(ledgerHash);
+ if (maxBlockHeight == GENESIS_BLOCK_HEIGHT) { // 只有一个创世区块
+ return totalCount;
+ }
+ long lastTotalCount = peerService.getQueryService().getTransactionCount(ledgerHash, maxBlockHeight - 1);
+ return totalCount - lastTotalCount;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/accounts/additional-count")
+ @Override
+ public long getAdditionalDataAccountCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHeight") long blockHeight) {
+ long currentDaCount = peerService.getQueryService().getDataAccountCount(ledgerHash, blockHeight);
+ if (blockHeight == GENESIS_BLOCK_HEIGHT) {
+ return currentDaCount;
+ }
+ long lastBlockHeight = blockHeight - 1;
+ long lastDaCount = peerService.getQueryService().getDataAccountCount(ledgerHash, lastBlockHeight);
+ return currentDaCount - lastDaCount;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/accounts/additional-count")
+ @Override
+ public long getAdditionalDataAccountCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHash") HashDigest blockHash) {
+ LedgerBlock currentBlock = peerService.getQueryService().getBlock(ledgerHash, blockHash);
+ long currentBlockDaCount = peerService.getQueryService().getDataAccountCount(ledgerHash, blockHash);
+ if (currentBlock.getHeight() == GENESIS_BLOCK_HEIGHT) {
+ return currentBlockDaCount;
+ }
+ HashDigest previousHash = currentBlock.getPreviousHash();
+ long lastBlockDaCount = peerService.getQueryService().getDataAccountCount(ledgerHash, previousHash);
+ // 当前区块数据账户数量减上个区块数据账户数量
+ return currentBlockDaCount - lastBlockDaCount;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts/additional-count")
+ @Override
+ public long getAdditionalDataAccountCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ LedgerInfo ledgerInfo = peerService.getQueryService().getLedger(ledgerHash);
+ long maxBlockHeight = ledgerInfo.getLatestBlockHeight();
+ long totalCount = peerService.getQueryService().getDataAccountTotalCount(ledgerHash);
+ if (maxBlockHeight == GENESIS_BLOCK_HEIGHT) { // 只有一个创世区块
+ return totalCount;
+ }
+ long lastTotalCount = peerService.getQueryService().getDataAccountCount(ledgerHash, maxBlockHeight - 1);
+ return totalCount - lastTotalCount;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/users/additional-count")
+ @Override
+ public long getAdditionalUserCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHeight") long blockHeight) {
+ long currentUserCount = peerService.getQueryService().getUserCount(ledgerHash, blockHeight);
+ if (blockHeight == GENESIS_BLOCK_HEIGHT) {
+ return currentUserCount;
+ }
+ long lastBlockHeight = blockHeight - 1;
+ long lastUserCount = peerService.getQueryService().getUserCount(ledgerHash, lastBlockHeight);
+ return currentUserCount - lastUserCount;
+ }
+
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/users/additional-count")
+ @Override
+ public long getAdditionalUserCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHash") HashDigest blockHash) {
+ LedgerBlock currentBlock = peerService.getQueryService().getBlock(ledgerHash, blockHash);
+ long currentBlockUserCount = peerService.getQueryService().getUserCount(ledgerHash, blockHash);
+ if (currentBlock.getHeight() == GENESIS_BLOCK_HEIGHT) {
+ return currentBlockUserCount;
+ }
+ HashDigest previousHash = currentBlock.getPreviousHash();
+ long lastBlockUserCount = peerService.getQueryService().getUserCount(ledgerHash, previousHash);
+ // 当前区块用户数量减上个区块用户数量
+ return currentBlockUserCount - lastBlockUserCount;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/users/additional-count")
+ @Override
+ public long getAdditionalUserCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ LedgerInfo ledgerInfo = peerService.getQueryService().getLedger(ledgerHash);
+ long maxBlockHeight = ledgerInfo.getLatestBlockHeight();
+ long totalCount = peerService.getQueryService().getUserTotalCount(ledgerHash);
+ if (maxBlockHeight == GENESIS_BLOCK_HEIGHT) { // 只有一个创世区块
+ return totalCount;
+ }
+ long lastTotalCount = peerService.getQueryService().getUserCount(ledgerHash, maxBlockHeight - 1);
+ return totalCount - lastTotalCount;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/contracts/additional-count")
+ @Override
+ public long getAdditionalContractCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHeight") long blockHeight) {
+ long currentContractCount = peerService.getQueryService().getContractCount(ledgerHash, blockHeight);
+ if (blockHeight == GENESIS_BLOCK_HEIGHT) {
+ return currentContractCount;
+ }
+ long lastBlockHeight = blockHeight - 1;
+ long lastContractCount = peerService.getQueryService().getUserCount(ledgerHash, lastBlockHeight);
+ return currentContractCount - lastContractCount;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/contracts/additional-count")
+ @Override
+ public long getAdditionalContractCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @PathVariable(name = "blockHash") HashDigest blockHash) {
+ LedgerBlock currentBlock = peerService.getQueryService().getBlock(ledgerHash, blockHash);
+ long currentBlockContractCount = peerService.getQueryService().getContractCount(ledgerHash, blockHash);
+ if (currentBlock.getHeight() == GENESIS_BLOCK_HEIGHT) {
+ return currentBlockContractCount;
+ }
+ HashDigest previousHash = currentBlock.getPreviousHash();
+ long lastBlockContractCount = peerService.getQueryService().getUserCount(ledgerHash, previousHash);
+ // 当前区块合约数量减上个区块合约数量
+ return currentBlockContractCount - lastBlockContractCount;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts/additional-count")
+ @Override
+ public long getAdditionalContractCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ LedgerInfo ledgerInfo = peerService.getQueryService().getLedger(ledgerHash);
+ long maxBlockHeight = ledgerInfo.getLatestBlockHeight();
+ long totalCount = peerService.getQueryService().getContractTotalCount(ledgerHash);
+ if (maxBlockHeight == GENESIS_BLOCK_HEIGHT) { // 只有一个创世区块
+ return totalCount;
+ }
+ long lastTotalCount = peerService.getQueryService().getContractCount(ledgerHash, maxBlockHeight - 1);
+ return totalCount - lastTotalCount;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "utils/pubkey/{pubkey}/addr")
+ public String getAddrByPubKey(@PathVariable(name = "pubkey") String strPubKey) {
+ PubKey pubKey = KeyGenCommand.decodePubKey(strPubKey);
+ return AddressEncoding.generateAddress(pubKey).toBase58();
+ }
+
+ @RequestMapping(method = RequestMethod.GET, value = "ledgers/{ledgerHash}/**/search")
+ public Object dataRetrieval(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,HttpServletRequest request) {
+ String result;
+ if (dataRetrievalUrl == null || dataRetrievalUrl.length() <= 0) {
+ result = "{'message':'OK','data':'" + "data.retrieval.url is empty" + "'}";
+ } else {
+ String queryParams = request.getQueryString() == null ? "": request.getQueryString();
+ String fullQueryUrl = new StringBuffer(dataRetrievalUrl)
+ .append(request.getRequestURI())
+ .append(BaseConstant.DELIMETER_QUESTION)
+ .append(queryParams)
+ .toString();
+ try {
+ result = dataRetrievalService.retrieval(fullQueryUrl);
+ ConsoleUtils.info("request = {%s} \r\n result = {%s} \r\n", fullQueryUrl, result);
+ } catch (Exception e) {
+ result = "{'message':'OK','data':'" + e.getMessage() + "'}";
+ }
+ }
+ return result;
+ }
+
+ public void setDataRetrievalUrl(String dataRetrievalUrl) {
+ this.dataRetrievalUrl = dataRetrievalUrl;
+ }
+
+ /**
+ * get all ledgers count;
+ */
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/count")
+ @Override
+ public int getLedgersCount() {
+ return peerService.getQueryService().getLedgerHashs().length;
+ }
+
+ /**
+ * get all ledgers hashs;
+ */
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers")
+ public HashDigest[] getLedgersHash(@RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex,
+ @RequestParam(name = "count", required = false, defaultValue = "-1") int count) {
+ return gatewayQueryService.getLedgersHash(fromIndex, count);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants/count")
+ public int getConsensusParticipantCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) {
+ return peerService.getQueryService().getConsensusParticipants(ledgerHash).length;
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants")
+ public ParticipantNode[] getConsensusParticipants(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex,
+ @RequestParam(name = "count", required = false, defaultValue = "-1") int count) {
+ return gatewayQueryService.getConsensusParticipants(ledgerHash,fromIndex,count);
+ }
+
+ /**
+ * get more users by fromIndex and count;
+ * @param ledgerHash
+ * @param fromIndex
+ * @param count
+ * @return
+ */
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/users")
+ @Override
+ public AccountHeader[] getUsers(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex,
+ @RequestParam(name = "count", required = false, defaultValue = "-1") int count) {
+ return peerService.getQueryService().getUsers(ledgerHash, fromIndex, count);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts")
+ @Override
+ public AccountHeader[] getDataAccounts(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex,
+ @RequestParam(name = "count", required = false, defaultValue = "-1") int count) {
+ return peerService.getQueryService().getDataAccounts(ledgerHash, fromIndex, count);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts")
+ @Override
+ public AccountHeader[] getContractAccounts(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,
+ @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex,
+ @RequestParam(name = "count", required = false, defaultValue = "-1") int count) {
+ return peerService.getQueryService().getContractAccounts(ledgerHash, fromIndex, count);
+ }
+}
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayGlobalExceptionHandler.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayGlobalExceptionHandler.java
new file mode 100644
index 00000000..463ad782
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayGlobalExceptionHandler.java
@@ -0,0 +1,43 @@
+package com.jd.blockchain.gateway.web;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import com.jd.blockchain.utils.BusinessException;
+import com.jd.blockchain.utils.web.model.ErrorCode;
+import com.jd.blockchain.utils.web.model.WebResponse;
+import com.jd.blockchain.utils.web.model.WebResponse.ErrorMessage;
+
+/**
+ * 全局异常处理类
+ */
+@RestControllerAdvice
+public class GatewayGlobalExceptionHandler {
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ @ExceptionHandler(value = Exception.class)
+ @ResponseBody
+ public WebResponse json(HttpServletRequest req, Exception ex) {
+ ErrorMessage message = null;
+ String reqURL = "[" + req.getMethod() + "] " + req.getRequestURL().toString();
+ if (ex instanceof BusinessException) {
+ logger.error("BusinessException occurred! --[RequestURL=" + reqURL + "][" + ex.getClass().toString() + "] "
+ + ex.getMessage(), ex);
+ BusinessException businessException = (BusinessException) ex;
+ message = new ErrorMessage(businessException.getErrorCode(), businessException.getMessage());
+ } else {
+ logger.error("Unexpected exception occurred! --[RequestURL=" + reqURL + "][" + ex.getClass().toString()
+ + "]" + ex.getMessage(), ex);
+ message = new ErrorMessage(ErrorCode.UNEXPECTED.getValue(),
+ ErrorCode.UNEXPECTED.getDescription(ex.getMessage()));
+ }
+ WebResponse response = WebResponse.createFailureResult(message);
+ return response;
+ }
+
+}
\ No newline at end of file
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayJsonResponseAdvice.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayJsonResponseAdvice.java
new file mode 100644
index 00000000..ec66c6cd
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayJsonResponseAdvice.java
@@ -0,0 +1,45 @@
+package com.jd.blockchain.gateway.web;
+
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+import com.jd.blockchain.utils.web.model.WebResponse;
+
+@RestControllerAdvice
+public class GatewayJsonResponseAdvice implements ResponseBodyAdvice {
+
+ @Override
+ public boolean supports(MethodParameter returnType, Class extends HttpMessageConverter>> converterType) {
+ if (MappingJackson2HttpMessageConverter.class == converterType
+ && (returnType.getContainingClass().getName().startsWith("com.jd")
+ || returnType.getDeclaringClass().getName().startsWith("com.jd"))) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
+ Class extends HttpMessageConverter>> selectedConverterType, ServerHttpRequest request,
+ ServerHttpResponse response) {
+ if (body == null) {
+ return WebResponse.createSuccessResult(null);
+ }
+ if (body instanceof ResponseEntity) {
+ return body;
+ }
+ // 把返回结果自动转换为 WebResponse;
+ if (body instanceof WebResponse) {
+ return body;
+ }
+ return WebResponse.createSuccessResult(body);
+ }
+
+}
\ No newline at end of file
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayTimeTasks.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayTimeTasks.java
new file mode 100644
index 00000000..819691e2
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayTimeTasks.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright: Copyright 2016-2020 JD.COM All Right Reserved
+ * FileName: com.jd.blockchain.gateway.web.GatewayTimeTasks
+ * Author: shaozhuguang
+ * Department: 区块链研发部
+ * Date: 2019/1/16 下午6:17
+ * Description:
+ */
+package com.jd.blockchain.gateway.web;
+
+import com.jd.blockchain.consensus.service.NodeServer;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.gateway.PeerConnector;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ *
+ * @author shaozhuguang
+ * @create 2019/1/16
+ * @since 1.0.0
+ */
+@Component
+@EnableScheduling
+public class GatewayTimeTasks {
+
+ private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(GatewayTimeTasks.class);
+
+ @Autowired
+ private PeerConnector peerConnector;
+
+ //每30分钟执行一次
+ @Scheduled(cron = "0 */8 * * * * ")
+ public void updateLedger(){
+ try {
+ peerConnector.reconnect();
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayWebSecurityConfigurer.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayWebSecurityConfigurer.java
new file mode 100644
index 00000000..857d18ae
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayWebSecurityConfigurer.java
@@ -0,0 +1,17 @@
+package com.jd.blockchain.gateway.web;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+@Configuration
+@EnableWebSecurity
+public class GatewayWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.authorizeRequests().anyRequest().permitAll();
+ http.csrf().disable();
+ }
+}
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayWebServerConfigurer.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayWebServerConfigurer.java
new file mode 100644
index 00000000..ed43d2a7
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayWebServerConfigurer.java
@@ -0,0 +1,71 @@
+package com.jd.blockchain.gateway.web;
+
+import java.util.List;
+
+import com.jd.blockchain.web.serializes.ByteArrayObjectUtil;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import com.jd.blockchain.utils.io.ByteArray;
+import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils;
+import com.jd.blockchain.utils.web.model.JsonWebResponseMessageConverter;
+import com.jd.blockchain.web.converters.BinaryMessageConverter;
+import com.jd.blockchain.web.converters.HashDigestInputConverter;
+
+/**
+ * @author zhuguang
+ * @date 2018-08-08
+ */
+@Configuration
+public class GatewayWebServerConfigurer implements WebMvcConfigurer {
+
+ static {
+ JSONSerializeUtils.disableCircularReferenceDetect();
+ JSONSerializeUtils.configStringSerializer(ByteArray.class);
+ }
+
+
+ @Override
+ public void extendMessageConverters(List> converters) {
+ int index = converters.size();
+ for (int i = 0; i < converters.size(); i++) {
+ if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
+ index = i;
+ break;
+ }
+ }
+
+ JsonWebResponseMessageConverter jsonConverter = new JsonWebResponseMessageConverter(false);
+
+ converters.add(index, jsonConverter);
+
+ converters.add(0, new BinaryMessageConverter());
+
+ initByteArrayJsonSerialize();
+ }
+
+ @Override
+ public void addFormatters(FormatterRegistry registry) {
+ registry.addConverter(new HashDigestInputConverter());
+ }
+
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
+ registry.addResourceHandler("/webjars/**")
+ .addResourceLocations("classpath:/META-INF/resources");
+ }
+
+ @Override
+ public void addViewControllers(ViewControllerRegistry registry) {
+ registry.addViewController("/").setViewName("web/index.html");
+ }
+
+ private void initByteArrayJsonSerialize() {
+ ByteArrayObjectUtil.init();
+ }
+}
diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/TxProcessingController.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/TxProcessingController.java
new file mode 100644
index 00000000..c2cba6f8
--- /dev/null
+++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/TxProcessingController.java
@@ -0,0 +1,70 @@
+package com.jd.blockchain.gateway.web;
+
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jd.blockchain.binaryproto.BinaryEncodingUtils;
+import com.jd.blockchain.crypto.CryptoUtils;
+import com.jd.blockchain.crypto.asymmetric.SignatureFunction;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.gateway.PeerService;
+import com.jd.blockchain.ledger.DigitalSignature;
+import com.jd.blockchain.ledger.TransactionContent;
+import com.jd.blockchain.ledger.TransactionRequest;
+import com.jd.blockchain.ledger.TransactionResponse;
+import com.jd.blockchain.ledger.data.TransactionService;
+import com.jd.blockchain.utils.BusinessException;
+import com.jd.blockchain.web.converters.BinaryMessageConverter;
+
+/**
+ * @author huanghaiquan
+ *
+ */
+@RestController
+public class TxProcessingController implements TransactionService {
+
+ @Autowired
+ private PeerService peerService;
+
+ @RequestMapping(path = "rpc/tx", method = RequestMethod.POST, consumes = BinaryMessageConverter.CONTENT_TYPE_VALUE, produces = BinaryMessageConverter.CONTENT_TYPE_VALUE)
+ @Override
+ public @ResponseBody TransactionResponse process(@RequestBody TransactionRequest txRequest) {
+ // 检查交易请求的信息是否完整;
+ HashDigest ledgerHash = txRequest.getTransactionContent().getLedgerHash();
+ if (ledgerHash == null) {
+ // 未指定交易的账本;
+ throw new IllegalArgumentException("The TransactionRequest miss ledger hash!");
+ }
+
+ // 预期的请求中不应该包含节点签名,首个节点签名应该由当前网关提供;
+ if (txRequest.getNodeSignatures() != null && txRequest.getNodeSignatures().length > 0) {
+ throw new IllegalArgumentException("Gateway cann't accept TransactionRequest with any NodeSignature!");
+ }
+
+ // TODO:检查参与者的签名;
+ DigitalSignature[] partiSigns = txRequest.getEndpointSignatures();
+ if (partiSigns == null || partiSigns.length == 0) {
+ // 缺少参与者签名,则采用检查托管账户并进行托管签名;如果请求未包含托管账户,或者托管账户认证失败,则返回401错误;
+ // TODO: 未实现!
+ throw new IllegalStateException("Not implemented!");
+ } else {
+ // 验证签名;
+ byte[] content = BinaryEncodingUtils.encode(txRequest.getTransactionContent(), TransactionContent.class);
+ for (DigitalSignature sign : partiSigns) {
+ SignatureFunction signFunc = CryptoUtils.sign(sign.getPubKey().getAlgorithm());
+ if (!signFunc.verify(sign.getDigest(), sign.getPubKey(), content)) {
+ throw new BusinessException("The validation of participant signatures fail!");
+ }
+ }
+ }
+
+ // 注:转发前自动附加网关的签名并转发请求至共识节点;异步的处理方式
+ return peerService.getTransactionService().process(txRequest);
+ }
+}
+
diff --git a/source/gateway/src/main/resources/application-gw.properties b/source/gateway/src/main/resources/application-gw.properties
new file mode 100644
index 00000000..b87537f3
--- /dev/null
+++ b/source/gateway/src/main/resources/application-gw.properties
@@ -0,0 +1 @@
+spring.mvc.favicon.enabled=false
\ No newline at end of file
diff --git a/source/gateway/src/main/resources/banner.txt b/source/gateway/src/main/resources/banner.txt
new file mode 100644
index 00000000..c39618bd
--- /dev/null
+++ b/source/gateway/src/main/resources/banner.txt
@@ -0,0 +1,13 @@
+
+ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄
+▐░░░░░░░░░░░▌▐░░░░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░▌ ▐░▌
+ ▀▀▀▀▀█░█▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌▐░█▀▀▀▀▀▀▀█░▌ ▀▀▀▀█░█▀▀▀▀ ▐░▌░▌ ▐░▌
+ ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌
+ ▐░▌ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄█░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌
+ ▐░▌ ▐░▌ ▐░▌ ▐░░░░░░░░░░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌
+ ▐░▌ ▐░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌
+ ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌
+ ▄▄▄▄▄█░▌ ▐░█▄▄▄▄▄▄▄█░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌▐░▌ ▐░▌ ▄▄▄▄█░█▄▄▄▄ ▐░▌ ▐░▐░▌
+▐░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░▌ ▐░░▌
+ ▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀
+
diff --git a/source/gateway/src/main/resources/gateway.conf b/source/gateway/src/main/resources/gateway.conf
new file mode 100644
index 00000000..84f00679
--- /dev/null
+++ b/source/gateway/src/main/resources/gateway.conf
@@ -0,0 +1,28 @@
+#网关的HTTP服务地址;
+http.host=127.0.0.1
+#网关的HTTP服务端口;
+http.port=8081
+#网关的HTTP服务上下文路径,可选;
+#http.context-path=
+
+#共识节点的服务地址;
+peer.host=127.0.0.1
+#共识节点的服务端口;
+peer.port=7080
+#共识节点的服务是否启用安全证书;
+peer.secure=false
+#共识节点的服务提供解析器
+peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider
+
+#数据检索服务对应URL
+#若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示
+data.retrieval.url=http://192.168.1.1:10001
+
+#默认公钥的内容(Base58编码数据);
+keys.default.pubkey=endPsK36g6bhgn5bj66uyX4uxqnkfGvdjpxWurAA5hbf8vVoVi8H
+#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一;
+keys.default.privkey-path=
+#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一;
+keys.default.privkey=177gjwmuvDnccAvrvmJyCN1dgAqqGzfYpe3pZ8dWWNBneM5GgdsS96vgjvBP4fX61jWfohQ
+#默认私钥的解码密码;
+keys.default.privkey-password=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY
\ No newline at end of file
diff --git a/source/gateway/src/main/resources/log4j2.xml b/source/gateway/src/main/resources/log4j2.xml
new file mode 100644
index 00000000..3fe4b122
--- /dev/null
+++ b/source/gateway/src/main/resources/log4j2.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/gateway/src/test/java/test/com/jd/blockchain/gateway/GatewayConfigPropertiesTest.java b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/GatewayConfigPropertiesTest.java
new file mode 100644
index 00000000..5bcb142c
--- /dev/null
+++ b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/GatewayConfigPropertiesTest.java
@@ -0,0 +1,58 @@
+package test.com.jd.blockchain.gateway;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.junit.Test;
+import org.springframework.core.io.ClassPathResource;
+
+import com.jd.blockchain.gateway.GatewayConfigProperties;
+import com.jd.blockchain.utils.codec.Base58Utils;
+import com.jd.blockchain.utils.io.BytesUtils;
+import com.jd.blockchain.utils.security.ShaUtils;
+
+public class GatewayConfigPropertiesTest {
+
+ @Test
+ public void test() {
+ ClassPathResource gatewayConfigResource = new ClassPathResource("gateway.conf");
+ try (InputStream in = gatewayConfigResource.getInputStream()) {
+ GatewayConfigProperties configProps = GatewayConfigProperties.resolve(in);
+ assertEquals("192.168.10.108", configProps.http().getHost());
+ assertEquals(80, configProps.http().getPort());
+ assertNull(configProps.http().getContextPath());
+
+ assertEquals("10.1.6.61", configProps.masterPeerAddress().getHost());
+ assertEquals(7100, configProps.masterPeerAddress().getPort());
+ assertTrue(configProps.masterPeerAddress().isSecure());
+
+ assertEquals("http://192.168.1.1:10001", configProps.dataRetrievalUrl());
+
+ assertEquals("keys/default.priv", configProps.keys().getDefault().getPrivKeyPath());
+ assertEquals("64hnH4a8n48LeEmNxUPcaZ1J", configProps.keys().getDefault().getPrivKeyValue());
+ assertEquals("a8n48LeEP", configProps.keys().getDefault().getPrivKeyPassword());
+
+ } catch (IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+
+ @Test
+ public void generateDefaultPassword() {
+ //generate default base58 password for gateway.conf
+ String password = "abc";
+ String encodePassword;
+ byte[] pwdBytes = BytesUtils.toBytes(password, "UTF-8");
+ encodePassword = Base58Utils.encode(ShaUtils.hash_256(pwdBytes));
+
+ return;
+
+ }
+
+}
diff --git a/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestDeserializer.java b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestDeserializer.java
new file mode 100644
index 00000000..82967c7f
--- /dev/null
+++ b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestDeserializer.java
@@ -0,0 +1,31 @@
+package test.com.jd.blockchain.gateway.data;
+
+import java.lang.reflect.Type;
+
+import com.alibaba.fastjson.parser.DefaultJSONParser;
+import com.alibaba.fastjson.parser.JSONToken;
+import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.utils.codec.Base58Utils;
+
+public class HashDigestDeserializer implements ObjectDeserializer{
+
+ public static final HashDigestDeserializer INSTANCE = new HashDigestDeserializer();
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
+ if (type instanceof Class && HashDigest.class.isAssignableFrom((Class>) type)) {
+ String base58Str = parser.parseObject(String.class);
+ byte[] hashBytes = Base58Utils.decode(base58Str);
+ return (T) new HashDigest(hashBytes);
+ }
+ return (T) parser.parse(fieldName);
+ }
+
+ @Override
+ public int getFastMatchToken() {
+ return JSONToken.LBRACE;
+ }
+
+}
diff --git a/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestJSONSerializeTest.java b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestJSONSerializeTest.java
new file mode 100644
index 00000000..8d7709d2
--- /dev/null
+++ b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestJSONSerializeTest.java
@@ -0,0 +1,66 @@
+package test.com.jd.blockchain.gateway.data;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.util.Random;
+
+import org.junit.Test;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.crypto.CryptoUtils;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils;
+
+public class HashDigestJSONSerializeTest {
+
+ private static class TestData {
+
+ private int id;
+
+ private HashDigest hash;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public HashDigest getHash() {
+ return hash;
+ }
+
+ public void setHash(HashDigest hash) {
+ this.hash = hash;
+ }
+
+ }
+
+ @Test
+ public void test() throws Exception {
+ JSONSerializeUtils.configSerialization(HashDigest.class, HashDigestSerializer.INSTANCE,
+ HashDigestDeserializer.INSTANCE);
+
+
+ HashDigest hash = new HashDigest(CryptoAlgorithm.SHA256, "jd-test".getBytes());
+
+ String hashJson = JSONSerializeUtils.serializeToJSON(hash, true);
+ HashDigest hashDigest = JSONSerializeUtils.deserializeFromJSON(hashJson, HashDigest.class);
+
+ assertArrayEquals(hash.getRawDigest(), hashDigest.getRawDigest());
+ assertEquals(hash.getAlgorithm(), hashDigest.getAlgorithm());
+
+ TestData data = new TestData();
+ data.setHash(hash);
+ data.setId(10);
+
+ String json = JSONSerializeUtils.serializeToJSON(data, true);
+
+ TestData desData = JSONSerializeUtils.deserializeFromJSON(json, TestData.class);
+ assertEquals(data.getHash(), desData.getHash());
+ assertEquals(data.getId(), desData.getId());
+ }
+
+}
diff --git a/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestSerializer.java b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestSerializer.java
new file mode 100644
index 00000000..8545d38d
--- /dev/null
+++ b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestSerializer.java
@@ -0,0 +1,27 @@
+package test.com.jd.blockchain.gateway.data;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+import com.alibaba.fastjson.serializer.JSONSerializer;
+import com.alibaba.fastjson.serializer.ObjectSerializer;
+import com.alibaba.fastjson.serializer.SerializeWriter;
+import com.jd.blockchain.crypto.hash.HashDigest;
+
+public class HashDigestSerializer implements ObjectSerializer {
+
+ public static HashDigestSerializer INSTANCE = new HashDigestSerializer();
+
+ @Override
+ public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features)
+ throws IOException {
+ SerializeWriter out = serializer.out;
+ if (object == null) {
+ out.writeNull();
+ return;
+ }
+ HashDigest hash = (HashDigest) object;
+ out.writeString(hash.toBase58());
+ }
+
+}
diff --git a/source/gateway/src/test/resources/gateway.conf b/source/gateway/src/test/resources/gateway.conf
new file mode 100644
index 00000000..b99068a2
--- /dev/null
+++ b/source/gateway/src/test/resources/gateway.conf
@@ -0,0 +1,29 @@
+
+#网关的HTTP服务地址;
+http.host=192.168.10.108
+#网关的HTTP服务端口;
+http.port=80
+#网关的HTTP服务上下文路径,可选;
+#http.context-path=
+
+#共识节点的服务地址;
+peer.host=10.1.6.61
+#共识节点的服务端口;
+peer.port=7100
+#共识节点的服务是否启用安全证书;
+peer.secure=true
+#共识节点的服务提供解析器
+peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider
+
+#数据检索服务对应URL
+#若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示
+data.retrieval.url=http://192.168.1.1:10001
+
+#默认公钥的内容(Base58编码数据);
+keys.default.pubkey=64hnNxUPcH4a8NxUPc
+#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一;
+keys.default.privkey-path=keys/default.priv
+#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一;
+keys.default.privkey=64hnH4a8n48LeEmNxUPcaZ1J
+#默认私钥的解码密码;
+keys.default.privkey-password=a8n48LeEP
\ No newline at end of file
diff --git a/source/ledger/ledger-core/pom.xml b/source/ledger/ledger-core/pom.xml
new file mode 100644
index 00000000..bd23883e
--- /dev/null
+++ b/source/ledger/ledger-core/pom.xml
@@ -0,0 +1,105 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ ledger
+ 0.8.2.RELEASE
+
+ ledger-core
+
+
+
+ com.jd.blockchain
+ ledger-model
+ ${project.version}
+
+
+ com.jd.blockchain
+ storage-service
+ ${project.version}
+
+
+ com.jd.blockchain
+ binary-proto
+ ${project.version}
+
+
+ com.jd.blockchain
+ crypto-framework
+ ${project.version}
+
+
+ com.jd.blockchain
+ storage-service
+ ${project.version}
+
+
+ com.jd.blockchain
+ contract-framework
+ ${project.version}
+
+
+ com.jd.blockchain
+ contract-jvm
+ ${project.version}
+
+
+ org.springframework
+ spring-context
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountAccessPolicy.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountAccessPolicy.java
new file mode 100644
index 00000000..eb2763d9
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountAccessPolicy.java
@@ -0,0 +1,26 @@
+package com.jd.blockchain.ledger.core;
+
+
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.ledger.AccountHeader;
+import com.jd.blockchain.utils.Bytes;
+
+/**
+ * 账户访问策略;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface AccountAccessPolicy {
+
+ /**
+ * Check access policy before committing the specified account;
+ *
+ * @param account
+ * @return Return true if it satisfies this policy, or false if it doesn't;
+ */
+ boolean checkCommitting(AccountHeader account);
+
+ boolean checkRegistering(Bytes address, PubKey pubKey);
+
+}
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountPrivilege.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountPrivilege.java
new file mode 100644
index 00000000..c57debff
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountPrivilege.java
@@ -0,0 +1,26 @@
+package com.jd.blockchain.ledger.core;
+
+public interface AccountPrivilege {
+
+ /**
+ * 数据“读”的操作码;
+ *
+ * @return
+ */
+ byte getReadingOpCode();
+
+ /**
+ * “写”的操作码;
+ *
+ * @return
+ */
+ byte getWrittingOpCode();
+
+ /**
+ * 其它的扩展操作码;
+ *
+ * @return
+ */
+ byte[] getExtOpCodes();
+
+}
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountSet.java
new file mode 100644
index 00000000..511a939f
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountSet.java
@@ -0,0 +1,456 @@
+package com.jd.blockchain.ledger.core;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.jd.blockchain.binaryproto.BinaryEncodingUtils;
+import com.jd.blockchain.binaryproto.DConstructor;
+import com.jd.blockchain.binaryproto.DataContractRegistry;
+import com.jd.blockchain.binaryproto.FieldSetter;
+import com.jd.blockchain.crypto.AddressEncoding;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.AccountHeader;
+import com.jd.blockchain.ledger.CryptoSetting;
+import com.jd.blockchain.ledger.LedgerTransaction;
+import com.jd.blockchain.storage.service.ExPolicyKVStorage;
+import com.jd.blockchain.storage.service.VersioningKVStorage;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.Transactional;
+
+public class AccountSet implements Transactional, MerkleProvable {
+
+ static {
+ DataContractRegistry.register(AccountHeader.class);
+ }
+
+ private final String keyPrefix;
+
+ private MerkleDataSet merkleDataset;
+
+ /**
+ * The cache of latest version accounts, including accounts getting by querying
+ * and by new regiestering ;
+ *
+ */
+ // TODO:未考虑大数据量时,由于缺少过期策略,会导致内存溢出的问题;
+ private Map latestAccountsCache = new HashMap<>();
+
+ private ExPolicyKVStorage baseExStorage;
+
+ private VersioningKVStorage baseVerStorage;
+
+ private CryptoSetting cryptoSetting;
+
+ private boolean updated;
+
+ private AccountAccessPolicy accessPolicy;
+
+ public boolean isReadonly() {
+ return merkleDataset.isReadonly();
+ }
+
+ public AccountSet(CryptoSetting cryptoSetting, String keyPrefix, ExPolicyKVStorage exStorage,
+ VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) {
+ this(null, cryptoSetting, keyPrefix, exStorage, verStorage, false, accessPolicy);
+ }
+
+ public AccountSet(HashDigest rootHash, CryptoSetting cryptoSetting, String keyPrefix, ExPolicyKVStorage exStorage,
+ VersioningKVStorage verStorage, boolean readonly, AccountAccessPolicy accessPolicy) {
+ this.keyPrefix = keyPrefix;
+ this.cryptoSetting = cryptoSetting;
+ this.baseExStorage = exStorage;
+ this.baseVerStorage = verStorage;
+ this.merkleDataset = new MerkleDataSet(rootHash, cryptoSetting, keyPrefix, this.baseExStorage,
+ this.baseVerStorage, readonly);
+ this.accessPolicy = accessPolicy;
+ }
+
+ // public HashDigest getAccountRootHash() {
+ // return merkleDataset.getRootHash();
+ // }
+
+ @Override
+ public HashDigest getRootHash() {
+ return merkleDataset.getRootHash();
+ }
+
+ @Override
+ public MerkleProof getProof(Bytes key) {
+ return merkleDataset.getProof(key);
+ }
+
+ public AccountHeader[] getAccounts(int fromIndex, int count) {
+ byte[][] results = merkleDataset.getLatestValues(fromIndex, count);
+ AccountHeader[] accounts = new AccountHeader[results.length];
+
+ for (int i = 0; i < results.length; i++) {
+ accounts[i] = deserialize(results[i]);
+ }
+ return accounts;
+ }
+
+// private VersioningAccount deserialize(byte[] txBytes) {
+//// return BinaryEncodingUtils.decode(txBytes, null, Account.class);
+// AccountHeaderData accInfo = BinaryEncodingUtils.decode(txBytes);
+//// return new BaseAccount(accInfo.getAddress(), accInfo.getPubKey(), null, cryptoSetting,
+//// baseExStorage, baseVerStorage, true, accessPolicy);
+// return new VersioningAccount(accInfo.getAddress(), accInfo.getPubKey(), accInfo.getRootHash(), cryptoSetting,
+// keyPrefix, baseExStorage, baseVerStorage, true, accessPolicy, accInfo.);
+// }
+
+ private AccountHeader deserialize(byte[] txBytes) {
+ return BinaryEncodingUtils.decode(txBytes);
+ }
+
+ /**
+ * 返回账户的总数量;
+ *
+ * @return
+ */
+ public long getTotalCount() {
+ return merkleDataset.getDataCount();
+ }
+
+ /**
+ * 返回最新版本的 Account;
+ *
+ * @param address
+ * @return
+ */
+ public BaseAccount getAccount(Bytes address) {
+ return this.getAccount(address, -1);
+ }
+
+ /**
+ * 账户是否存在;
+ *
+ * 如果指定的账户已经注册(通过 {@link #register(String, PubKey)} 方法),但尚未提交(通过
+ * {@link #commit()} 方法),此方法对该账户仍然返回 false;
+ *
+ * @param address
+ * @return
+ */
+ public boolean contains(Bytes address) {
+ long latestVersion = getVersion(address);
+ return latestVersion > -1;
+ }
+
+ /**
+ * 返回指定账户的版本;
+ * 如果账户已经注册,则返回该账户的最新版本,值大于等于 0;
+ * 如果账户不存在,则返回 -1;
+ * 如果指定的账户已经注册(通过 {@link #register(String, PubKey)} 方法),但尚未提交(通过
+ * {@link #commit()} 方法),此方法对该账户仍然返回 0;
+ *
+ * @param address
+ * @return
+ */
+ public long getVersion(Bytes address) {
+ VersioningAccount acc = latestAccountsCache.get(address);
+ if (acc != null) {
+ // 已注册尚未提交,也返回 -1;
+ return acc.version == -1 ? 0 : acc.version;
+ }
+
+ return merkleDataset.getVersion(address);
+ }
+
+ /**
+ * 返回指定版本的 Account;
+ *
+ * 只有最新版本的账户才能可写的,其它都是只读;
+ *
+ * @param address
+ * 账户地址;
+ * @param version
+ * 账户版本;如果指定为 -1,则返回最新版本;
+ * @return
+ */
+ public BaseAccount getAccount(Bytes address, long version) {
+ version = version < 0 ? -1 : version;
+ VersioningAccount acc = latestAccountsCache.get(address);
+ if (acc != null && version == -1) {
+ return acc;
+ } else if (acc != null && acc.version == version) {
+ return acc;
+ }
+
+ long latestVersion = merkleDataset.getVersion(address);
+ if (latestVersion < 0) {
+ // Not exist;
+ return null;
+ }
+ if (version > latestVersion) {
+ return null;
+ }
+
+ // 如果是不存在的,或者刚刚新增未提交的账户,则前面一步查询到的 latestVersion 小于 0, 代码不会执行到此;
+ if (acc != null && acc.version != latestVersion) {
+ // 当执行到此处时,并且缓冲列表中缓存了最新的版本,
+ // 如果当前缓存的最新账户的版本和刚刚从存储中检索得到的最新版本不一致,可能存在外部的并发更新,这超出了系统设计的逻辑;
+
+ // TODO:如果是今后扩展至集群方案时,这种不一致的原因可能是由其它集群节点实例执行了更新,这种情况下,最好是放弃旧缓存,并重新加载和缓存最新版本;
+ // by huanghaiquan at 2018-9-2 23:03:00;
+ throw new IllegalStateException("The latest version in cache is not equals the latest version in storage! "
+ + "Mybe some asynchronzing updating are performed out of current server.");
+ }
+
+ // Now, be sure that "acc == null", so get account from storage;
+
+ byte[] bytes = merkleDataset.getValue(address, version);
+ if (bytes == null) {
+ return null;
+ }
+
+ // Set readonly for the old version account;
+ boolean readonly = (version > -1 && version < latestVersion) || isReadonly();
+
+ // String prefix = address.concat(LedgerConsts.KEY_SEPERATOR);
+ // ExPolicyKVStorage ss = PrefixAppender.prefix(prefix, baseExStorage);
+ // VersioningKVStorage vs = PrefixAppender.prefix(prefix, baseVerStorage);
+ // BaseAccount accDS = deserialize(bytes, cryptoSetting, ss, vs, readonly);
+ String prefix = keyPrefix + address;
+ acc = deserialize(bytes, cryptoSetting, prefix, baseExStorage, baseVerStorage, readonly, latestVersion);
+ if (!readonly) {
+ // cache the latest version witch enable reading and writing;
+ // readonly version of account not necessary to be cached;
+ latestAccountsCache.put(address, acc);
+ }
+ return acc;
+ }
+
+ /**
+ * 注册一个新账户;
+ *
+ * 如果账户已经存在,则会引发 {@link LedgerException} 异常;
+ *
+ * 如果指定的地址和公钥不匹配,则会引发 {@link LedgerException} 异常;
+ *
+ * @param address
+ * 区块链地址;
+ * @param pubKey
+ * 公钥;
+ * @return 注册成功的账户对象;
+ */
+ public BaseAccount register(Bytes address, PubKey pubKey) {
+ if (isReadonly()) {
+ throw new IllegalArgumentException("This AccountSet is readonly!");
+ }
+
+ verifyAddressEncoding(address, pubKey);
+
+ VersioningAccount cachedAcc = latestAccountsCache.get(address);
+ if (cachedAcc != null) {
+ if (cachedAcc.version < 0) {
+ // 同一个新账户已经注册,但尚未提交,所以重复注册不会引起任何变化;
+ return cachedAcc;
+ }
+ // 相同的账户已经存在;
+ throw new LedgerException("The registering account already exist!");
+ }
+ long version = merkleDataset.getVersion(address);
+ if (version >= 0) {
+ throw new LedgerException("The registering account already exist!");
+ }
+
+ if (!accessPolicy.checkRegistering(address, pubKey)) {
+ throw new LedgerException("Account Registering was rejected for the access policy!");
+ }
+
+ // String prefix = address.concat(LedgerConsts.KEY_SEPERATOR);
+ // ExPolicyKVStorage accExStorage = PrefixAppender.prefix(prefix,
+ // baseExStorage);
+ // VersioningKVStorage accVerStorage = PrefixAppender.prefix(prefix,
+ // baseVerStorage);
+ // BaseAccount accDS = createInstance(address, pubKey, cryptoSetting,
+ // accExStorage, accVerStorage);
+
+ String prefix = keyPrefix + address;
+ VersioningAccount acc = createInstance(address, pubKey, cryptoSetting, prefix, baseExStorage, baseVerStorage, -1);
+ latestAccountsCache.put(address, acc);
+ updated = true;
+
+ return acc;
+ }
+
+ private void verifyAddressEncoding(Bytes address, PubKey pubKey) {
+ Bytes chAddress = AddressEncoding.generateAddress(pubKey);
+ if (!chAddress.equals(address)) {
+ throw new LedgerException("The registering Address mismatch the specified PubKey!");
+ }
+ }
+
+ private VersioningAccount createInstance(Bytes address, PubKey pubKey, CryptoSetting cryptoSetting, String keyPrefix,
+ ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, long version) {
+ return new VersioningAccount(address, pubKey, cryptoSetting, keyPrefix, exStorage, verStorage, accessPolicy, version);
+ }
+
+ private VersioningAccount deserialize(byte[] bytes, CryptoSetting cryptoSetting, String keyPrefix,
+ ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly, long version) {
+ AccountHeader accInfo = BinaryEncodingUtils.decode(bytes);
+ return new VersioningAccount(accInfo.getAddress(), accInfo.getPubKey(), accInfo.getRootHash(), cryptoSetting,
+ keyPrefix, exStorage, verStorage, readonly, accessPolicy, version);
+ }
+
+ private byte[] serialize(AccountHeader account) {
+ return BinaryEncodingUtils.encode(account, AccountHeader.class);
+ }
+
+ @Override
+ public boolean isUpdated() {
+ return updated;
+ }
+
+ @Override
+ public void commit() {
+ if (!updated) {
+ return;
+ }
+ try {
+ for (VersioningAccount acc : latestAccountsCache.values()) {
+ // updated or new created;
+ if (acc.isUpdated() || acc.version < 0) {
+ // 提交更改,更新哈希;
+ acc.commit();
+ byte[] value = serialize(acc);
+ long ver = merkleDataset.setValue(acc.getAddress(), value, acc.version);
+ if (ver < 0) {
+ // Update fail;
+ throw new LedgerException("Account updating fail! --[Address=" + acc.getAddress() + "]");
+ }
+ }
+ }
+ merkleDataset.commit();
+ } finally {
+ updated = false;
+ latestAccountsCache.clear();
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (!updated) {
+ return;
+ }
+ String[] addresses = new String[latestAccountsCache.size()];
+ latestAccountsCache.keySet().toArray(addresses);
+ for (String address : addresses) {
+ VersioningAccount acc = latestAccountsCache.remove(address);
+ // cancel;
+ if (acc.isUpdated()) {
+ acc.cancel();
+ }
+ }
+ updated = false;
+ }
+
+ public static class AccountHeaderData implements AccountHeader {
+
+ private Bytes address;
+ private PubKey pubKey;
+ private HashDigest rootHash;
+
+ @DConstructor(name = "AccountHeaderData")
+ public AccountHeaderData(@FieldSetter(name = "getAddress", type = "String") Bytes address,
+ @FieldSetter(name = "getPubKey", type = "PubKey") PubKey pubKey,
+ @FieldSetter(name = "getRootHash", type = "HashDigest") HashDigest rootHash) {
+ this.address = address;
+ this.pubKey = pubKey;
+ this.rootHash = rootHash;
+ }
+
+ @Override
+ public Bytes getAddress() {
+ return address;
+ }
+
+ @Override
+ public PubKey getPubKey() {
+ return pubKey;
+ }
+
+ @Override
+ public HashDigest getRootHash() {
+ return rootHash;
+ }
+
+ }
+
+ private class VersioningAccount extends BaseAccount {
+
+ // private final BaseAccount account;
+
+ private final long version;
+
+ // public VersioningAccount(BaseAccount account, long version) {
+ // this.account = account;
+ // this.version = version;
+ // }
+
+ public VersioningAccount(Bytes address, PubKey pubKey, HashDigest rootHash, CryptoSetting cryptoSetting,
+ String keyPrefix, ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly,
+ AccountAccessPolicy accessPolicy, long version) {
+ super(address, pubKey, rootHash, cryptoSetting, keyPrefix, exStorage, verStorage, readonly, accessPolicy);
+ this.version = version;
+ }
+
+ public VersioningAccount(Bytes address, PubKey pubKey, CryptoSetting cryptoSetting, String keyPrefix,
+ ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy,
+ long version) {
+ super(address, pubKey, cryptoSetting, keyPrefix, exStorage, verStorage, accessPolicy);
+ this.version = version;
+ }
+
+ // @Override
+ // public Bytes getAddress() {
+ // return account.getAddress();
+ // }
+ //
+ // @Override
+ // public PubKey getPubKey() {
+ // return account.getPubKey();
+ // }
+ //
+ // @Override
+ // public HashDigest getRootHash() {
+ // return account.getRootHash();
+ // }
+ //
+ // @Override
+ // public MerkleProof getProof(Bytes key) {
+ // return account.getProof(key);
+ // }
+ //
+ // @Override
+ // public boolean isReadonly() {
+ // return account.isReadonly();
+ // }
+
+ @Override
+ public long setBytes(Bytes key, byte[] value, long version) {
+ long v = super.setBytes(key, value, version);
+ if (v > -1) {
+ updated = true;
+ }
+ return v;
+ }
+
+ // @Override
+ // public long getKeyVersion(Bytes key) {
+ // return account.getKeyVersion(key);
+ // }
+ //
+ // @Override
+ // public byte[] getBytes(Bytes key) {
+ // return account.getBytes(key);
+ // }
+ //
+ // @Override
+ // public byte[] getBytes(Bytes key, long version) {
+ // return account.getBytes(key, version);
+ // }
+
+ }
+
+}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AuthorizableDataSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AuthorizableDataSet.java
new file mode 100644
index 00000000..ca0a406a
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AuthorizableDataSet.java
@@ -0,0 +1,168 @@
+//package com.jd.blockchain.ledger.core;
+//
+//import com.jd.blockchain.crypto.hash.HashDigest;
+//
+//import my.utils.Scratchable;
+//import my.utils.io.ByteArray;
+//import my.utils.io.BytesUtils;
+//import my.utils.io.ExistancePolicyKVStorage;
+//import my.utils.io.VersioningKVStorage;
+//
+///**
+// * 可进行授权控制的数据集合;
+// *
+// * @author huanghaiquan
+// *
+// */
+//public class AuthorizableDataSet implements Scratchable {
+//
+// public static final String DATA_PREFIX = "DATA" + LedgerConsts.KEY_SEPERATOR;
+//// public static final String PRIVILEGE_PREFIX = "PRVL" + LedgerConsts.KEY_SEPERATOR;
+//
+// private static final String DEFAULT_PRIVILEGE_KEY = "%";
+//
+// private DataAccessable accessable;
+//
+// protected MerkleDataSet data;
+//
+//// private PrivilegeDataSet privileges;
+//
+// /**
+// * Create a new Account instance;
+// *
+// * @param address
+// * @param pubKey
+// */
+// protected AuthorizableDataSet(CryptoSetting merkleTreeSetting, ExistancePolicyKVStorage simpleStorage,
+// VersioningKVStorage versioningStorage) {
+// this(null, merkleTreeSetting, null, simpleStorage, versioningStorage);
+// }
+//
+// protected AuthorizableDataSet(byte[] dataRootHash, CryptoSetting merkleTreeSetting, byte[] privilegeRootHash,
+// ExistancePolicyKVStorage simpleStorage, VersioningKVStorage versioningStorage) {
+// this(dataRootHash, merkleTreeSetting, privilegeRootHash, simpleStorage, versioningStorage, false);
+// }
+//
+// protected AuthorizableDataSet(byte[] dataRootHash, CryptoSetting merkleTreeSetting, byte[] privilegeRootHash,
+// ExistancePolicyKVStorage simpleStorage, VersioningKVStorage versioningStorage, boolean readonly) {
+// this.data = new MerkleDataSet(dataRootHash, merkleTreeSetting,
+// PrefixAppender.prefix(DATA_PREFIX, simpleStorage),
+// PrefixAppender.prefix(DATA_PREFIX, versioningStorage), readonly);
+//
+//// this.privileges = new PrivilegeDataSet(privilegeRootHash, merkleTreeSetting,
+//// PrefixAppender.prefix(PRIVILEGE_PREFIX, simpleStorage),
+//// PrefixAppender.prefix(PRIVILEGE_PREFIX, versioningStorage), readonly);
+// }
+//
+// public ByteArray getDataRootHash() {
+// return data.getRootHash();
+// }
+//
+//// public ByteArray getPrivilegeRootHash() {
+//// return privileges.getRootHash();
+//// }
+//
+// /**
+// *
+// * @param userAddress
+// * @param op
+// * @param enable
+// */
+// public void setPrivilege(String userAddress, byte op, boolean enable) {
+//
+// }
+//
+// /**
+// *
+// * @param op
+// * @param enable
+// */
+// public void setDefaultPrivilege(byte op, boolean enable) {
+// }
+//
+// public boolean checkCurrentUserPrivilege() {
+// return false;
+// }
+//
+// /**
+// * Return the latest version entry associated the specified key; If the key
+// * doesn't exist, then return -1;
+// *
+// * @param key
+// * @return
+// */
+// public long getVersion(String key) {
+// return data.getVersion(key);
+// }
+//
+// protected long setString(String key, String value, long version) {
+// checkWritting();
+// byte[] bytes = BytesUtils.toBytes(value, LedgerConsts.CHARSET);
+// return data.setValue(key, bytes, version);
+// }
+//
+// protected String getString(String key) {
+// checkReading();
+// byte[] value = data.getValue(key);
+// return BytesUtils.toString(value, LedgerConsts.CHARSET);
+// }
+//
+// protected String getString(String key, long version) {
+// checkReading();
+// byte[] value = data.getValue(key, version);
+// return BytesUtils.toString(value, LedgerConsts.CHARSET);
+// }
+//
+// protected long setValue(String key, byte[] value, long version) {
+// checkWritting();
+// return data.setValue(key, value, version);
+// }
+//
+// protected byte[] getValue(String key) {
+// checkReading();
+// return data.getValue(key);
+// }
+//
+// protected byte[] getValue(String key, long version) {
+// checkReading();
+// return data.getValue(key, version);
+// }
+//
+// private void checkWritting() {
+// // Check writting enable;
+// }
+//
+// private void checkReading() {
+// // TODO Check privilege of reading;
+// }
+//
+// // /**
+// // * 数据“读”的操作码;
+// // *
+// // * @return
+// // */
+// // protected abstract AccountPrivilege getPrivilege();
+//
+// @Override
+// public boolean isUpdated() {
+// return data.isUpdated();
+//// return data.isUpdated()|| privileges.isUpdated();
+// }
+//
+// @Override
+// public void commit() {
+// if (data.isUpdated()) {
+// data.commit();
+// }
+//// if (privileges.isUpdated()) {
+//// privileges.commit();
+//// }
+// }
+//
+// @Override
+// public void cancel() {
+// data.cancel();
+//// privileges.cancel();
+// }
+//
+//}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Authorization.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Authorization.java
new file mode 100644
index 00000000..cba2ffe4
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Authorization.java
@@ -0,0 +1,40 @@
+package com.jd.blockchain.ledger.core;
+
+import com.jd.blockchain.ledger.DigitalSignature;
+
+/**
+ * {@link Authorization} 抽象了对特定用户/角色的授权信息;
+ *
+ * @author huanghaiquan
+ *
+ */
+public interface Authorization {
+
+ /**
+ * 被授权用户/角色的地址;
+ *
+ * @return
+ */
+ String getAddress();
+
+ /**
+ * 授权码;
+ *
+ * @return
+ */
+ byte[] getCode();
+
+ /**
+ * 授权者的签名;
+ *
+ * @return
+ */
+ DigitalSignature getSignature();
+
+ // /**
+ // * 授权生成的时间戳;
+ // * @return
+ // */
+ // long getTs();
+
+}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AuthorizationVO.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AuthorizationVO.java
new file mode 100644
index 00000000..24d7f125
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AuthorizationVO.java
@@ -0,0 +1,42 @@
+package com.jd.blockchain.ledger.core;
+
+import com.jd.blockchain.ledger.DigitalSignature;
+
+public class AuthorizationVO implements Authorization {
+
+ private String address;
+
+ private byte[] code;
+
+ private DigitalSignature signature;
+
+
+ @Override
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ @Override
+ public byte[] getCode() {
+ return code;
+ }
+
+ public void setCode(byte[] code) {
+ this.code = code;
+ }
+
+ @Override
+ public DigitalSignature getSignature() {
+ return signature;
+ }
+
+
+ public void setSignature(DigitalSignature signature) {
+ this.signature = signature;
+ }
+
+}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/BaseAccount.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/BaseAccount.java
new file mode 100644
index 00000000..5417dc2a
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/BaseAccount.java
@@ -0,0 +1,222 @@
+package com.jd.blockchain.ledger.core;
+
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.AccountHeader;
+import com.jd.blockchain.ledger.BlockchainIdentity;
+import com.jd.blockchain.ledger.BlockchainIdentityData;
+import com.jd.blockchain.ledger.CryptoSetting;
+import com.jd.blockchain.storage.service.ExPolicyKVStorage;
+import com.jd.blockchain.storage.service.VersioningKVStorage;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.Transactional;
+
+/**
+ * 事务性的基础账户;
+ *
+ * @author huanghaiquan
+ *
+ */
+public class BaseAccount implements AccountHeader, MerkleProvable, Transactional {
+
+ private BlockchainIdentity bcid;
+
+ protected MerkleDataSet dataset;
+
+ private AccountAccessPolicy accessPolicy;
+
+ /**
+ * Create a new Account with the specified address and pubkey;
+ *
+ * At the same time, a empty merkle dataset is also created for this account,
+ * which is used for storing data of this account.
+ *
+ * Note that, the blockchain identity of the account is not stored in the
+ * account's merkle dataset, but is stored by the outer invoker;
+ *
+ * @param address
+ * @param pubKey
+ */
+ public BaseAccount(Bytes address, PubKey pubKey, CryptoSetting cryptoSetting, String keyPrefix,
+ ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) {
+ this(address, pubKey, null, cryptoSetting, keyPrefix, exStorage, verStorage, false, accessPolicy);
+ }
+
+ /**
+ * Create a new Account with the specified address and pubkey;
+ *
+ * At the same time, a empty merkle dataset is also created for this account,
+ * which is used for storing data of this account.
+ *
+ * Note that, the blockchain identity of the account is not stored in the
+ * account's merkle dataset, but is stored by the outer invoker;
+ *
+ * @param bcid
+ * @param cryptoSetting
+ * @param exStorage
+ * @param verStorage
+ * @param accessPolicy
+ */
+ public BaseAccount(BlockchainIdentity bcid, CryptoSetting cryptoSetting, String keyPrefix,
+ ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) {
+ this(bcid, null, cryptoSetting, keyPrefix, exStorage, verStorage, false, accessPolicy);
+ }
+
+ /**
+ * Create a account instance with the specified address and pubkey and load it's
+ * merkle dataset with the specified root hash. which is used for storing data
+ * of this account.
+ *
+ * @param address
+ * @param pubKey
+ * @param dataRootHash
+ * merkle root hash of account's data; if null be set, create a new
+ * empty merkle dataset;
+ * @param cryptoSetting
+ * @param exStorage
+ * @param verStorage
+ * @param readonly
+ * @param accessPolicy
+ */
+ public BaseAccount(Bytes address, PubKey pubKey, HashDigest dataRootHash, CryptoSetting cryptoSetting,
+ String keyPrefix, ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly,
+ AccountAccessPolicy accessPolicy) {
+ this(new BlockchainIdentityData(address, pubKey), dataRootHash, cryptoSetting, keyPrefix, exStorage, verStorage,
+ readonly, accessPolicy);
+ }
+
+ public BaseAccount(BlockchainIdentity bcid, HashDigest dataRootHash, CryptoSetting cryptoSetting, String keyPrefix,
+ ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly,
+ AccountAccessPolicy accessPolicy) {
+ this.bcid = bcid;
+ this.dataset = new MerkleDataSet(dataRootHash, cryptoSetting, keyPrefix, exStorage, verStorage, readonly);
+ this.accessPolicy = accessPolicy;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jd.blockchain.ledger.core.AccountDataSet#getAddress()
+ */
+ @Override
+ public Bytes getAddress() {
+ return bcid.getAddress();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jd.blockchain.ledger.core.AccountDataSet#getPubKey()
+ */
+ @Override
+ public PubKey getPubKey() {
+ return bcid.getPubKey();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.jd.blockchain.ledger.core.AccountDataSet#getRootHash()
+ */
+ @Override
+ public HashDigest getRootHash() {
+ return dataset.getRootHash();
+ }
+
+ @Override
+ public MerkleProof getProof(Bytes key) {
+ return dataset.getProof(key);
+ }
+
+ /**
+ * 是否只读;
+ *
+ * @return
+ */
+ public boolean isReadonly() {
+ return dataset.isReadonly();
+ }
+
+ /**
+ * Create or update the value associated the specified key if the version
+ * checking is passed.
+ *
+ * The value of the key will be updated only if it's latest version equals the
+ * specified version argument.
+ * If the key doesn't exist, the version checking will be ignored, and key will
+ * be created with a new sequence number as id.
+ * It also could specify the version argument to -1 to ignore the version
+ * checking.
+ *
+ * If updating is performed, the version of the key increase by 1.
+ * If creating is performed, the version of the key initialize by 0.
+ *
+ * @param key
+ * The key of data;
+ * @param value
+ * The value of data;
+ * @param version
+ * The expected version of the key.
+ * @return The new version of the key.
+ * If the key is new created success, then return 0;
+ * If the key is updated success, then return the new version;
+ * If this operation fail by version checking or other reason, then
+ * return -1;
+ */
+ public long setBytes(Bytes key, byte[] value, long version) {
+ // TODO: 支持多种数据类型;
+ return dataset.setValue(key, value, version);
+ }
+
+ /**
+ * Return the latest version entry associated the specified key; If the key
+ * doesn't exist, then return -1;
+ *
+ * @param key
+ * @return
+ */
+ public long getKeyVersion(Bytes key) {
+ return dataset.getVersion(key);
+ }
+
+ /**
+ * return the latest version's value;
+ *
+ * @param key
+ * @return return null if not exist;
+ */
+ public byte[] getBytes(Bytes key) {
+ return dataset.getValue(key);
+ }
+
+ /**
+ * Return the specified version's value;
+ *
+ * @param key
+ * @param version
+ * @return return null if not exist;
+ */
+ public byte[] getBytes(Bytes key, long version) {
+ return dataset.getValue(key, version);
+ }
+
+ @Override
+ public boolean isUpdated() {
+ return dataset.isUpdated();
+ }
+
+ @Override
+ public void commit() {
+ if (!accessPolicy.checkCommitting(this)) {
+ throw new LedgerException("Account Committing was rejected for the access policy!");
+ }
+
+ dataset.commit();
+ }
+
+ @Override
+ public void cancel() {
+ dataset.cancel();
+ }
+
+}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Consensus.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Consensus.java
new file mode 100644
index 00000000..da7b6104
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Consensus.java
@@ -0,0 +1,21 @@
+package com.jd.blockchain.ledger.core;
+
+
+/**
+ * @author hhq
+ * @version 1.0
+ * @created 14-6��-2018 12:13:32
+ */
+public class Consensus {
+
+ public P2PRealm m_P2PRealm;
+
+ public Consensus(){
+
+ }
+
+ public void finalize() throws Throwable {
+
+ }
+
+}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ConsensusConfig.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ConsensusConfig.java
new file mode 100644
index 00000000..a947556e
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ConsensusConfig.java
@@ -0,0 +1,28 @@
+//package com.jd.blockchain.ledger.core;
+//
+//import com.jd.blockchain.ledger.ConsensusSetting;
+//
+//public class ConsensusConfig implements ConsensusSetting {
+//
+// private byte[] value;
+//
+// public ConsensusConfig() {
+// }
+//
+// public ConsensusConfig(ConsensusSetting setting) {
+// if (setting != null) {
+// this.value = setting.getValue();
+// }
+// }
+//
+//
+// @Override
+// public byte[] getValue() {
+// return value;
+// }
+//
+//
+// public void setValue(byte[] value) { this.value = value;}
+//
+//
+//}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ContractAccount.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ContractAccount.java
new file mode 100644
index 00000000..bdabb6dc
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ContractAccount.java
@@ -0,0 +1,80 @@
+package com.jd.blockchain.ledger.core;
+
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.AccountHeader;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+public class ContractAccount implements AccountHeader {
+
+ // private static final String CONTRACT_INFO_PREFIX = "INFO" +
+ // LedgerConsts.KEY_SEPERATOR;
+ private static final Bytes CONTRACT_INFO_PREFIX = Bytes.fromString("INFO" + LedgerConsts.KEY_SEPERATOR);
+
+ // private static final String CHAIN_CODE_KEY = "CHAIN-CODE";
+ private static final Bytes CHAIN_CODE_KEY = Bytes.fromString("CHAIN-CODE");
+
+ private BaseAccount accBase;
+
+ public ContractAccount(BaseAccount accBase) {
+ this.accBase = accBase;
+ }
+
+ @Override
+ public Bytes getAddress() {
+ return accBase.getAddress();
+ }
+
+ @Override
+ public PubKey getPubKey() {
+ return accBase.getPubKey();
+ }
+
+ @Override
+ public HashDigest getRootHash() {
+ return accBase.getRootHash();
+ }
+
+ public MerkleProof getChaincodeProof() {
+ return accBase.getProof(CHAIN_CODE_KEY);
+ }
+
+ public MerkleProof getPropertyProof(Bytes key) {
+ return accBase.getProof(encodePropertyKey(key));
+ }
+
+ public long setChaincode(byte[] chaincode, long version) {
+ return accBase.setBytes(CHAIN_CODE_KEY, chaincode, version);
+ }
+
+ public byte[] getChainCode() {
+ return accBase.getBytes(CHAIN_CODE_KEY);
+ }
+
+ public byte[] getChainCode(long version) {
+ return accBase.getBytes(CHAIN_CODE_KEY, version);
+ }
+
+ public long getChaincodeVersion() {
+ return accBase.getKeyVersion(CHAIN_CODE_KEY);
+ }
+
+ public long setProperty(Bytes key, String value, long version) {
+ return accBase.setBytes(encodePropertyKey(key), BytesUtils.toBytes(value), version);
+ }
+
+ public String getProperty(Bytes key) {
+ return BytesUtils.toString(accBase.getBytes(encodePropertyKey(key)));
+ }
+
+ public String getProperty(Bytes key, long version) {
+ return BytesUtils.toString(accBase.getBytes(encodePropertyKey(key), version));
+ }
+
+ private Bytes encodePropertyKey(Bytes key) {
+ return CONTRACT_INFO_PREFIX.concat(key);
+// return key.concatTo(CONTRACT_INFO_PREFIX);
+ }
+
+}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ContractAccountSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ContractAccountSet.java
new file mode 100644
index 00000000..612de4b8
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ContractAccountSet.java
@@ -0,0 +1,122 @@
+package com.jd.blockchain.ledger.core;
+
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.AccountHeader;
+import com.jd.blockchain.ledger.CryptoSetting;
+import com.jd.blockchain.ledger.DigitalSignature;
+import com.jd.blockchain.storage.service.ExPolicyKVStorage;
+import com.jd.blockchain.storage.service.VersioningKVStorage;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.Transactional;
+
+public class ContractAccountSet implements MerkleProvable, Transactional {
+
+ private AccountSet accountSet;
+
+ public ContractAccountSet(CryptoSetting cryptoSetting, String prefix, ExPolicyKVStorage exStorage,
+ VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) {
+ accountSet = new AccountSet(cryptoSetting, prefix, exStorage, verStorage, accessPolicy);
+ }
+
+ public ContractAccountSet(HashDigest dataRootHash, CryptoSetting cryptoSetting, String prefix,
+ ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly,
+ AccountAccessPolicy accessPolicy) {
+ accountSet = new AccountSet(dataRootHash, cryptoSetting, prefix, exStorage, verStorage, readonly, accessPolicy);
+ }
+
+ public AccountHeader[] getAccounts(int fromIndex, int count) {
+ return accountSet.getAccounts(fromIndex,count);
+ }
+
+ public boolean isReadonly() {
+ return accountSet.isReadonly();
+ }
+
+ @Override
+ public HashDigest getRootHash() {
+ return accountSet.getRootHash();
+ }
+
+ /**
+ * 返回合约总数;
+ *
+ * @return
+ */
+ public long getTotalCount() {
+ return accountSet.getTotalCount();
+ }
+
+ @Override
+ public MerkleProof getProof(Bytes address) {
+ return accountSet.getProof(address);
+ }
+
+ public boolean contains(Bytes address) {
+ return accountSet.contains(address);
+ }
+
+ public ContractAccount getContract(Bytes address) {
+ BaseAccount accBase = accountSet.getAccount(address);
+ return new ContractAccount(accBase);
+ }
+
+ public ContractAccount getContract(Bytes address, long version) {
+ BaseAccount accBase = accountSet.getAccount(address, version);
+ return new ContractAccount(accBase);
+ }
+
+ /**
+ * 部署一项新的合约链码;
+ *
+ * @param address
+ * 合约账户地址;
+ * @param pubKey
+ * 合约账户公钥;
+ * @param addressSignature
+ * 地址签名;合约账户的私钥对地址的签名;
+ * @param chaincode
+ * 链码内容;
+ * @return 合约账户;
+ */
+ public ContractAccount deploy(Bytes address, PubKey pubKey, DigitalSignature addressSignature, byte[] chaincode) {
+ // TODO: 校验和记录合约地址签名;
+ BaseAccount accBase = accountSet.register(address, pubKey);
+ ContractAccount contractAcc = new ContractAccount(accBase);
+ contractAcc.setChaincode(chaincode, -1);
+ return contractAcc;
+ }
+
+ /**
+ * 更新指定账户的链码;
+ *
+ * @param address
+ * 合约账户地址;
+ * @param chaincode
+ * 链码内容;
+ * @param version
+ * 链码版本;
+ * @return 返回链码的新版本号;
+ */
+ public long update(Bytes address, byte[] chaincode, long version) {
+ BaseAccount accBase = accountSet.getAccount(address);
+ ContractAccount contractAcc = new ContractAccount(accBase);
+ return contractAcc.setChaincode(chaincode, version);
+ }
+
+ @Override
+ public boolean isUpdated() {
+ return accountSet.isUpdated();
+ }
+
+ @Override
+ public void commit() {
+ accountSet.commit();
+ }
+
+ @Override
+ public void cancel() {
+ accountSet.cancel();
+ }
+
+}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/CryptoConfig.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/CryptoConfig.java
new file mode 100644
index 00000000..b296dfc1
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/CryptoConfig.java
@@ -0,0 +1,40 @@
+package com.jd.blockchain.ledger.core;
+
+import com.jd.blockchain.crypto.CryptoAlgorithm;
+import com.jd.blockchain.ledger.CryptoSetting;
+
+public class CryptoConfig implements CryptoSetting {
+
+ private CryptoAlgorithm hashAlgorithm;
+
+ private boolean autoVerifyHash;
+
+ public CryptoConfig() {
+ }
+
+ public CryptoConfig(CryptoSetting setting) {
+ this.hashAlgorithm = setting.getHashAlgorithm();
+ this.autoVerifyHash = setting.getAutoVerifyHash();
+ }
+
+
+ @Override
+ public CryptoAlgorithm getHashAlgorithm() {
+ return hashAlgorithm;
+ }
+
+ @Override
+ public boolean getAutoVerifyHash() {
+ return autoVerifyHash;
+ }
+
+ public void setHashAlgorithm(CryptoAlgorithm hashAlgorithm) {
+ this.hashAlgorithm = hashAlgorithm;
+ }
+
+ public void setAutoVerifyHash(boolean autoVerifyHash) {
+ this.autoVerifyHash = autoVerifyHash;
+ }
+
+
+}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccount.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccount.java
new file mode 100644
index 00000000..2b2271ae
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccount.java
@@ -0,0 +1,155 @@
+package com.jd.blockchain.ledger.core;
+
+import com.jd.blockchain.binaryproto.BinaryEncodingUtils;
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.AccountHeader;
+import com.jd.blockchain.ledger.BytesValue;
+import com.jd.blockchain.ledger.KVDataEntry;
+import com.jd.blockchain.ledger.KVDataObject;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.QueryUtil;
+import com.jd.blockchain.utils.ValueType;
+
+public class DataAccount implements AccountHeader, MerkleProvable {
+ private BaseAccount baseAccount;
+
+ public DataAccount(BaseAccount accBase) {
+ this.baseAccount = accBase;
+ }
+
+ @Override
+ public Bytes getAddress() {
+ return baseAccount.getAddress();
+ }
+
+ @Override
+ public PubKey getPubKey() {
+ return baseAccount.getPubKey();
+ }
+
+ @Override
+ public HashDigest getRootHash() {
+ return baseAccount.getRootHash();
+ }
+
+ /**
+ * 返回指定数据的存在性证明;
+ */
+ @Override
+ public MerkleProof getProof(Bytes key) {
+ return baseAccount.getProof(key);
+ }
+
+ public long setBytes(Bytes key, byte[] value, long version) {
+ return baseAccount.setBytes(key, value, version);
+ }
+
+ /**
+ * Return the latest version entry associated the specified key; If the key
+ * doesn't exist, then return -1;
+ *
+ * @param key
+ * @return
+ */
+ public long getDataVersion(String key) {
+ return baseAccount.getKeyVersion(Bytes.fromString(key));
+ }
+
+ /**
+ * Return the latest version entry associated the specified key; If the key
+ * doesn't exist, then return -1;
+ *
+ * @param key
+ * @return
+ */
+ public long getDataVersion(Bytes key) {
+ return baseAccount.getKeyVersion(key);
+ }
+
+ /**
+ * return the latest version's value;
+ *
+ * @param key
+ * @return return null if not exist;
+ */
+ public byte[] getBytes(String key) {
+ return baseAccount.getBytes(Bytes.fromString(key));
+ }
+
+ /**
+ * return the latest version's value;
+ *
+ * @param key
+ * @return return null if not exist;
+ */
+ public byte[] getBytes(Bytes key) {
+ return baseAccount.getBytes(key);
+ }
+
+ /**
+ * return the specified version's value;
+ *
+ * @param key
+ * @param version
+ * @return return null if not exist;
+ */
+ public byte[] getBytes(String key, long version) {
+ return baseAccount.getBytes(Bytes.fromString(key), version);
+ }
+
+ /**
+ * return the specified version's value;
+ *
+ * @param key
+ * @param version
+ * @return return null if not exist;
+ */
+ public byte[] getBytes(Bytes key, long version) {
+ return baseAccount.getBytes(key, version);
+ }
+
+ /**
+ * return the specified index's KVDataEntry;
+ *
+ * @param fromIndex
+ * @param count
+ * @return return null if not exist;
+ */
+
+ public KVDataEntry[] getDataEntries(int fromIndex, int count) {
+ if (getDataEntriesTotalCount() == 0 || count == 0) {
+ return null;
+ }
+ int pages[] = QueryUtil.calFromIndexAndCount(fromIndex,count,(int)getDataEntriesTotalCount());
+ fromIndex = pages[0];
+ count = pages[1];
+
+ KVDataEntry[] kvDataEntries = new KVDataEntry[count];
+ byte[] value;
+ String key;
+ long ver;
+ for (int i = 0; i < count; i++) {
+ value = baseAccount.dataset.getValuesAtIndex(fromIndex);
+ key = baseAccount.dataset.getKeyAtIndex(fromIndex);
+ ver = baseAccount.dataset.getVersion(key);
+ BytesValue decodeData = BinaryEncodingUtils.decode(value);
+ kvDataEntries[i] = new KVDataObject(key, ver, ValueType.valueOf(decodeData.getType().CODE), decodeData.getValue().toBytes());
+ fromIndex++;
+ }
+
+ return kvDataEntries;
+ }
+
+ /**
+ * return the dataAccount's kv total count;
+ *
+ * @param
+ * @param
+ * @return return total count;
+ */
+ public long getDataEntriesTotalCount() {
+ return baseAccount.dataset.getDataCount();
+ }
+
+}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccountSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccountSet.java
new file mode 100644
index 00000000..8d92eb5a
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccountSet.java
@@ -0,0 +1,83 @@
+package com.jd.blockchain.ledger.core;
+
+import com.jd.blockchain.crypto.asymmetric.PubKey;
+import com.jd.blockchain.crypto.hash.HashDigest;
+import com.jd.blockchain.ledger.AccountHeader;
+import com.jd.blockchain.ledger.CryptoSetting;
+import com.jd.blockchain.ledger.DigitalSignature;
+import com.jd.blockchain.storage.service.ExPolicyKVStorage;
+import com.jd.blockchain.storage.service.VersioningKVStorage;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.Transactional;
+
+public class DataAccountSet implements MerkleProvable, Transactional {
+
+ private AccountSet accountSet;
+
+ public DataAccountSet(CryptoSetting cryptoSetting, String prefix,ExPolicyKVStorage exStorage,
+ VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) {
+ accountSet = new AccountSet(cryptoSetting,prefix, exStorage, verStorage, accessPolicy);
+ }
+
+ public DataAccountSet(HashDigest dataRootHash, CryptoSetting cryptoSetting, String prefix,
+ ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly,
+ AccountAccessPolicy accessPolicy) {
+ accountSet = new AccountSet(dataRootHash, cryptoSetting, prefix, exStorage, verStorage, readonly, accessPolicy);
+ }
+
+ public AccountHeader[] getAccounts(int fromIndex, int count) {
+ return accountSet.getAccounts(fromIndex,count);
+ }
+
+ public boolean isReadonly() {
+ return accountSet.isReadonly();
+ }
+
+ @Override
+ public HashDigest getRootHash() {
+ return accountSet.getRootHash();
+ }
+
+ public long getTotalCount() {
+ return accountSet.getTotalCount();
+ }
+
+ /**
+ * 返回账户的存在性证明;
+ */
+ @Override
+ public MerkleProof getProof(Bytes address) {
+ return accountSet.getProof(address);
+ }
+
+ public DataAccount register(Bytes address, PubKey pubKey, DigitalSignature addressSignature) {
+ // TODO: 未实现对地址签名的校验和记录;
+ BaseAccount accBase = accountSet.register(address, pubKey);
+ return new DataAccount(accBase);
+ }
+
+ public DataAccount getDataAccount(Bytes address) {
+ BaseAccount accBase = accountSet.getAccount(address);
+ return new DataAccount(accBase);
+ }
+
+ public DataAccount getDataAccount(Bytes address, long version) {
+ BaseAccount accBase = accountSet.getAccount(address, version);
+ return new DataAccount(accBase);
+ }
+
+ @Override
+ public boolean isUpdated() {
+ return accountSet.isUpdated();
+ }
+
+ @Override
+ public void commit() {
+ accountSet.commit();
+ }
+
+ @Override
+ public void cancel() {
+ accountSet.cancel();
+ }
+}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Gateway.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Gateway.java
new file mode 100644
index 00000000..6874d8aa
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Gateway.java
@@ -0,0 +1,22 @@
+package com.jd.blockchain.ledger.core;
+
+import com.jd.blockchain.ledger.ParticipantNode;
+
+/**
+ * @author hhq
+ * @version 1.0
+ * @created 14-6��-2018 12:13:32
+ */
+public class Gateway extends Node {
+
+ public ParticipantNode m_Participant;
+
+ public Gateway(){
+
+ }
+
+ public void finalize() throws Throwable {
+ super.finalize();
+ }
+
+}
\ No newline at end of file
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericAccountDataset.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericAccountDataset.java
new file mode 100644
index 00000000..76831c3e
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericAccountDataset.java
@@ -0,0 +1,36 @@
+//package com.jd.blockchain.ledger.core;
+//
+//public class GenericAccountDataset {
+//
+// private Class dataClazz;
+//
+// private AccountDataSet dataset;
+//
+// protected GenericAccountDataset(AccountDataSet dataset, Class dataClazz) {
+// this.dataClazz = dataClazz;
+// this.dataset = dataset;
+// }
+//
+// protected T getData(String key) {
+// byte[] value = dataset.getBytes(key);
+// return deserialize(value);
+// }
+//
+// protected T getData(String key, long version) {
+// byte[] value = dataset.getBytes(key, version);
+// return deserialize(value);
+// }
+//
+// protected long setData(String key, T data, long version) {
+// byte[] value = serialize(data);
+// return dataset.setBytes(key, value, version);
+// }
+//
+// private byte[] serialize(T data) {
+// throw new IllegalStateException("Not implemented!");
+// }
+//
+// private T deserialize(byte[] value) {
+// throw new IllegalStateException("Not implemented!");
+// }
+//}
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericDataEntry.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericDataEntry.java
new file mode 100644
index 00000000..3e73d582
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericDataEntry.java
@@ -0,0 +1,11 @@
+//package com.jd.blockchain.ledger.core;
+//
+//public interface GenericDataEntry {
+//
+// String getKey();
+//
+// long getVersion();
+//
+// T getValue();
+//
+//}
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericMerkleDataEntry.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericMerkleDataEntry.java
new file mode 100644
index 00000000..8a8c5635
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericMerkleDataEntry.java
@@ -0,0 +1,9 @@
+//package com.jd.blockchain.ledger.core;
+//
+//public interface GenericMerkleDataEntry {
+//
+// GenericDataEntry getData();
+//
+// MerkleProof getProof();
+//
+//}
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericMerkleDataSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericMerkleDataSet.java
new file mode 100644
index 00000000..31df6427
--- /dev/null
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericMerkleDataSet.java
@@ -0,0 +1,259 @@
+//package com.jd.blockchain.ledger.core;
+//
+//import com.jd.blockchain.binaryproto.BinaryEncodingUtils;
+//import com.jd.blockchain.crypto.hash.HashDigest;
+//
+//import my.utils.Transactional;
+//import my.utils.io.ByteArray;
+//import my.utils.io.ExistentialKVStorage;
+//import my.utils.io.VersioningKVEntry;
+//import my.utils.io.VersioningKVStorage;
+//
+///**
+// * @author huanghaiquan
+// *
+// * @param
+// * @param
+// */
+//public class GenericMerkleDataSet implements Transactional {
+//
+// /**
+// * 数据契约的接口类型;
+// */
+// private Class dataContractClazz;
+//
+// /**
+// * 数据契约的实现类型;
+// */
+// private Class