# Conflicts: # source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.javatags/1.0.0
@@ -9,6 +9,8 @@ package com.jd.blockchain.consts; | |||||
public interface DataCodes { | public interface DataCodes { | ||||
public static final int BYTES_VALUE = 0x80; | public static final int BYTES_VALUE = 0x80; | ||||
public static final int BYTES_VALUE_LIST = 0x81; | |||||
public static final int BLOCK_CHAIN_IDENTITY = 0x90; | public static final int BLOCK_CHAIN_IDENTITY = 0x90; | ||||
@@ -16,8 +18,6 @@ public interface DataCodes { | |||||
public static final int BLOCK_BODY = 0x110; | public static final int BLOCK_BODY = 0x110; | ||||
// public static final int BLOCK_LEDGER = 0x110; | |||||
public static final int BLOCK_GENESIS = 0x120; | public static final int BLOCK_GENESIS = 0x120; | ||||
public static final int DATA_SNAPSHOT = 0x130; | public static final int DATA_SNAPSHOT = 0x130; | ||||
@@ -30,6 +30,8 @@ public interface DataCodes { | |||||
public static final int TX_CONTENT_BODY = 0x220; | public static final int TX_CONTENT_BODY = 0x220; | ||||
public static final int TX_RETURN_MESSAGE = 0x230; | |||||
public static final int TX_OP = 0x300; | public static final int TX_OP = 0x300; | ||||
public static final int TX_OP_LEDGER_INIT = 0x301; | public static final int TX_OP_LEDGER_INIT = 0x301; | ||||
@@ -49,6 +51,8 @@ public interface DataCodes { | |||||
public static final int TX_RESPONSE = 0x350; | public static final int TX_RESPONSE = 0x350; | ||||
public static final int TX_OP_RESULT = 0x360; | |||||
public static final int METADATA = 0x600; | public static final int METADATA = 0x600; | ||||
public static final int METADATA_INIT_SETTING = 0x610; | public static final int METADATA_INIT_SETTING = 0x610; | ||||
@@ -81,17 +85,9 @@ public interface DataCodes { | |||||
//contract related; | //contract related; | ||||
public static final int CONTRACT = 0xA00; | public static final int CONTRACT = 0xA00; | ||||
public static final int CONTRACT_INT8 = 0xA01; | |||||
public static final int CONTRACT_INT16 = 0xA02; | |||||
public static final int CONTRACT_INT32 = 0xA03; | |||||
public static final int CONTRACT_INT64 = 0xA04; | |||||
public static final int CONTRACT_TEXT = 0xA05; | |||||
public static final int CONTRACT_BINARY = 0xA06; | |||||
public static final int CONTRACT_BIG_INT = 0xA07; | |||||
//...0xA19 | |||||
public static final int CONTRACT_BIZ_CONTENT = 0xA20; | |||||
public static final int CONTRACT_ARGS = 0xA21; | |||||
//...0xA19 | |||||
public static final int HASH = 0xB00; | public static final int HASH = 0xB00; | ||||
public static final int HASH_OBJECT = 0xB10; | public static final int HASH_OBJECT = 0xB10; | ||||
@@ -0,0 +1,63 @@ | |||||
package com.jd.blockchain.binaryproto; | |||||
/** | |||||
* 基础类型标志; | |||||
* | |||||
* @author huanghaiquan | |||||
* | |||||
*/ | |||||
public interface BaseType { | |||||
/** | |||||
* 空值; | |||||
*/ | |||||
public static final byte NIL = (byte) 0x00; | |||||
/** | |||||
* 布尔; | |||||
*/ | |||||
public static final byte BOOLEAN = (byte) 0x01; | |||||
/** | |||||
* 整数; | |||||
*/ | |||||
public static final byte INTEGER = (byte) 0x10; | |||||
/** | |||||
* 8位整数; | |||||
*/ | |||||
public static final byte INT8 = (byte) (INTEGER | 0x01); | |||||
/** | |||||
* 16位整数; | |||||
*/ | |||||
public static final byte INT16 = (byte) (INTEGER | 0x02); | |||||
/** | |||||
* 32位整数; | |||||
*/ | |||||
public static final byte INT32 = (byte) (INTEGER | 0x03); | |||||
/** | |||||
* 64位整数; | |||||
*/ | |||||
public static final byte INT64 = (byte) (INTEGER | 0x04); | |||||
/** | |||||
* 文本 | |||||
*/ | |||||
public static final byte TEXT = (byte) 0x20; | |||||
/** | |||||
* 字节序列; | |||||
*/ | |||||
public static final byte BYTES = (byte) 0x40; | |||||
/** | |||||
* 扩展类型;<br> | |||||
* | |||||
* 最高位为1,用作保留字段; | |||||
*/ | |||||
public static final byte EXT = (byte) 0x80; | |||||
} |
@@ -57,5 +57,7 @@ public class BinaryProtocol { | |||||
} | } | ||||
} | } |
@@ -1,24 +0,0 @@ | |||||
package com.jd.blockchain.binaryproto; | |||||
public interface DataType { | |||||
/** | |||||
* 空值; | |||||
*/ | |||||
public static final byte NIL = (byte) 0x00; | |||||
/** | |||||
* 布尔; | |||||
*/ | |||||
public static final byte BOOLEAN = (byte) 0x01; | |||||
/** | |||||
* 数值; | |||||
*/ | |||||
public static final byte NUMERIC = (byte) 0x10; | |||||
public static final byte TEXT = (byte) 0x20; | |||||
public static final byte BINARY = (byte) 0x40; | |||||
} |
@@ -11,68 +11,42 @@ public enum PrimitiveType { | |||||
/** | /** | ||||
* 空; | * 空; | ||||
*/ | */ | ||||
NIL(DataType.NIL), | |||||
NIL(BaseType.NIL), | |||||
/** | /** | ||||
* 布尔型; | * 布尔型; | ||||
*/ | */ | ||||
BOOLEAN(DataType.BOOLEAN), | |||||
BOOLEAN(BaseType.BOOLEAN), | |||||
/** | /** | ||||
* 数值型: | |||||
* 8位的整数: | |||||
*/ | */ | ||||
INT8((byte) (DataType.NUMERIC | 0x01)), | |||||
INT16((byte) (DataType.NUMERIC | 0x02)), | |||||
INT32((byte) (DataType.NUMERIC | 0x03)), | |||||
INT64((byte) (DataType.NUMERIC | 0x04)), | |||||
INT8(BaseType.INT8), | |||||
/** | /** | ||||
* 时间戳; | |||||
* 16位整数; | |||||
*/ | */ | ||||
TIMESTAMP((byte) (DataType.NUMERIC | 0x08)), | |||||
INT16(BaseType.INT16), | |||||
/** | /** | ||||
* 文本数据; | |||||
* 32位整数; | |||||
*/ | */ | ||||
TEXT(DataType.TEXT), | |||||
INT32(BaseType.INT32), | |||||
/** | /** | ||||
* 文本数据; | |||||
* 64位整数; | |||||
*/ | */ | ||||
JSON((byte) (DataType.TEXT | 0x01)), | |||||
INT64(BaseType.INT64), | |||||
/** | /** | ||||
* 文本数据; | |||||
* 文本; | |||||
*/ | */ | ||||
XML((byte) (DataType.TEXT | 0x02)), | |||||
TEXT(BaseType.TEXT), | |||||
/** | /** | ||||
* 二进制数据; | * 二进制数据; | ||||
*/ | */ | ||||
BYTES(DataType.BINARY), | |||||
/** | |||||
* 大整数; | |||||
*/ | |||||
BIG_INT((byte) (DataType.BINARY | 0x01)), | |||||
/** | |||||
* 图片; | |||||
*/ | |||||
IMG((byte) (DataType.BINARY | 0x02)), | |||||
/** | |||||
* 视频; | |||||
*/ | |||||
VIDEO((byte) (DataType.BINARY | 0x03)), | |||||
/** | |||||
* 位置坐标; | |||||
*/ | |||||
LOCATION((byte) (DataType.BINARY | 0x04)); | |||||
BYTES(BaseType.BYTES); | |||||
public final byte CODE; | public final byte CODE; | ||||
@@ -1,16 +1,14 @@ | |||||
package com.jd.blockchain.contract.engine; | package com.jd.blockchain.contract.engine; | ||||
import com.jd.blockchain.contract.ContractEventContext; | import com.jd.blockchain.contract.ContractEventContext; | ||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
import java.util.concurrent.CompletableFuture; | |||||
import java.util.concurrent.Future; | |||||
public interface ContractCode { | public interface ContractCode { | ||||
Bytes getAddress(); | Bytes getAddress(); | ||||
long getVersion(); | long getVersion(); | ||||
void processEvent(ContractEventContext eventContext, CompletableFuture<String> execReturn); | |||||
BytesValue processEvent(ContractEventContext eventContext); | |||||
} | } |
@@ -1,6 +1,5 @@ | |||||
package com.jd.blockchain.contract.engine; | package com.jd.blockchain.contract.engine; | ||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
/** | /** | ||||
@@ -27,9 +26,9 @@ public interface ContractEngine { | |||||
* 如果已经存在,则直接返回已有实例; | * 如果已经存在,则直接返回已有实例; | ||||
* | * | ||||
* @param address | * @param address | ||||
* @param code | |||||
* @param codeBytes | |||||
* @return | * @return | ||||
*/ | */ | ||||
ContractCode setupContract(Bytes address, long version, byte[] code); | |||||
ContractCode setupContract(Bytes address, long version, byte[] codeBytes); | |||||
} | } |
@@ -0,0 +1,105 @@ | |||||
package com.jd.blockchain.contract.jvm; | |||||
import java.lang.reflect.Method; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.util.ReflectionUtils; | |||||
import com.jd.blockchain.contract.ContractEventContext; | |||||
import com.jd.blockchain.contract.ContractException; | |||||
import com.jd.blockchain.contract.EventProcessingAware; | |||||
import com.jd.blockchain.contract.engine.ContractCode; | |||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.ledger.BytesValueEncoding; | |||||
import com.jd.blockchain.ledger.BytesValueList; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
/** | |||||
* @author huanghaiquan | |||||
* | |||||
*/ | |||||
public abstract class AbstractContractCode implements ContractCode { | |||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractContractCode.class); | |||||
private Bytes address; | |||||
private long version; | |||||
private ContractDefinition contractDefinition; | |||||
public AbstractContractCode(Bytes address, long version, ContractDefinition contractDefinition) { | |||||
this.address = address; | |||||
this.version = version; | |||||
this.contractDefinition = contractDefinition; | |||||
} | |||||
public ContractDefinition getContractDefinition() { | |||||
return contractDefinition; | |||||
} | |||||
@Override | |||||
public Bytes getAddress() { | |||||
return address; | |||||
} | |||||
@Override | |||||
public long getVersion() { | |||||
return version; | |||||
} | |||||
@Override | |||||
public BytesValue processEvent(ContractEventContext eventContext) { | |||||
EventProcessingAware evtProcAwire = null; | |||||
Object retn = null; | |||||
Method handleMethod = null; | |||||
Exception error = null; | |||||
try { | |||||
// 执行预处理; | |||||
Object contractInstance = getContractInstance(); | |||||
if (contractInstance instanceof EventProcessingAware) { | |||||
evtProcAwire = (EventProcessingAware) contractInstance; | |||||
} | |||||
if (evtProcAwire != null) { | |||||
evtProcAwire.beforeEvent(eventContext); | |||||
} | |||||
// 反序列化参数; | |||||
handleMethod = contractDefinition.getType().getHandleMethod(eventContext.getEvent()); | |||||
if (handleMethod == null) { | |||||
throw new ContractException( | |||||
String.format("Contract[%s:%s] has no handle method to handle event[%s]!", address.toString(), | |||||
contractDefinition.getType().getName(), eventContext.getEvent())); | |||||
} | |||||
BytesValueList bytesValues = eventContext.getArgs(); | |||||
Object[] args = BytesValueEncoding.decode(bytesValues, handleMethod.getParameterTypes()); | |||||
retn = ReflectionUtils.invokeMethod(handleMethod, contractInstance, args); | |||||
} catch (Exception e) { | |||||
error = e; | |||||
} | |||||
if (evtProcAwire != null) { | |||||
try { | |||||
evtProcAwire.postEvent(eventContext, error); | |||||
} catch (Exception e) { | |||||
String errorMessage = "Error occurred while posting contract event! --" + e.getMessage(); | |||||
LOGGER.error(errorMessage, e); | |||||
throw new ContractException(errorMessage, e); | |||||
} | |||||
} | |||||
if (error != null) { | |||||
// Rethrow error; | |||||
throw new ContractException(String.format("Error occurred while processing event[%s] of contract[%s]! --%s", | |||||
eventContext.getEvent(), address.toString(), error.getMessage()), error); | |||||
} | |||||
BytesValue retnBytes = BytesValueEncoding.encodeSingle(retn, handleMethod.getReturnType()); | |||||
return retnBytes; | |||||
} | |||||
protected abstract Object getContractInstance(); | |||||
} |
@@ -0,0 +1,24 @@ | |||||
package com.jd.blockchain.contract.jvm; | |||||
import com.jd.blockchain.contract.ContractType; | |||||
public class ContractDefinition { | |||||
private ContractType type; | |||||
private Class<?> mainClass; | |||||
public Class<?> getMainClass() { | |||||
return mainClass; | |||||
} | |||||
public ContractType getType() { | |||||
return type; | |||||
} | |||||
public ContractDefinition(ContractType type, Class<?> mainClass) { | |||||
this.type = type; | |||||
this.mainClass = mainClass; | |||||
} | |||||
} |
@@ -2,7 +2,6 @@ package com.jd.blockchain.contract.jvm; | |||||
import com.jd.blockchain.contract.engine.ContractCode; | import com.jd.blockchain.contract.engine.ContractCode; | ||||
import com.jd.blockchain.contract.engine.ContractEngine; | import com.jd.blockchain.contract.engine.ContractEngine; | ||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.runtime.Module; | import com.jd.blockchain.runtime.Module; | ||||
import com.jd.blockchain.runtime.RuntimeContext; | import com.jd.blockchain.runtime.RuntimeContext; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
@@ -1,42 +1,60 @@ | |||||
package com.jd.blockchain.contract.jvm; | package com.jd.blockchain.contract.jvm; | ||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.contract.ContractEventContext; | |||||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||||
import com.jd.blockchain.contract.engine.ContractCode; | |||||
import com.jd.blockchain.runtime.Module; | |||||
import com.jd.blockchain.transaction.ContractType; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.IllegalDataException; | |||||
import java.util.concurrent.Callable; | |||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
import org.springframework.util.ReflectionUtils; | |||||
import java.lang.reflect.Method; | |||||
import java.util.List; | |||||
import java.util.concurrent.CompletableFuture; | |||||
import java.util.concurrent.Future; | |||||
import com.jd.blockchain.contract.Contract; | |||||
import com.jd.blockchain.contract.ContractEventContext; | |||||
import com.jd.blockchain.contract.ContractException; | |||||
import com.jd.blockchain.contract.ContractType; | |||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.runtime.Module; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
/** | /** | ||||
* contract code based jvm | |||||
* 基于 java jar 包并且以模块化方式独立加载的合约代码; | |||||
* | * | ||||
* @author zhaogw | |||||
* @author huanghaiquan | |||||
* | |||||
*/ | */ | ||||
public class JavaContractCode implements ContractCode { | |||||
public class JavaContractCode extends AbstractContractCode { | |||||
private static final Logger LOGGER = LoggerFactory.getLogger(JavaContractCode.class); | private static final Logger LOGGER = LoggerFactory.getLogger(JavaContractCode.class); | ||||
private Module codeModule; | private Module codeModule; | ||||
private Bytes address; | private Bytes address; | ||||
private long version; | private long version; | ||||
private ContractEventContext contractEventContext; | |||||
private ContractType contractType; | |||||
public JavaContractCode(Bytes address, long version, Module codeModule) { | public JavaContractCode(Bytes address, long version, Module codeModule) { | ||||
super(address, version, resolveContractDefinition(codeModule)); | |||||
this.address = address; | this.address = address; | ||||
this.version = version; | this.version = version; | ||||
this.codeModule = codeModule; | this.codeModule = codeModule; | ||||
} | } | ||||
protected static ContractDefinition resolveContractDefinition(Module codeModule) { | |||||
String mainClassName = codeModule.getMainClass(); | |||||
Class<?> mainClass = codeModule.loadClass(mainClassName); | |||||
Class<?>[] interfaces = mainClass.getInterfaces(); | |||||
Class<?> contractInterface = null; | |||||
for (Class<?> itf : interfaces) { | |||||
Contract annoContract = itf.getAnnotation(Contract.class); | |||||
if (annoContract != null) { | |||||
if (contractInterface == null) { | |||||
contractInterface = itf; | |||||
} else { | |||||
throw new ContractException( | |||||
"One contract definition is only allowed to implement one contract type!"); | |||||
} | |||||
} | |||||
} | |||||
if (contractInterface == null) { | |||||
throw new ContractException("No contract type is implemented!"); | |||||
} | |||||
ContractType type = ContractType.resolve(contractInterface); | |||||
return new ContractDefinition(type, mainClass); | |||||
} | |||||
@Override | @Override | ||||
public Bytes getAddress() { | public Bytes getAddress() { | ||||
return address; | return address; | ||||
@@ -48,87 +66,42 @@ public class JavaContractCode implements ContractCode { | |||||
} | } | ||||
@Override | @Override | ||||
public void processEvent(ContractEventContext eventContext, CompletableFuture<String> execReturn) { | |||||
this.contractEventContext = eventContext; | |||||
codeModule.execute(new ContractExecution(execReturn)); | |||||
public BytesValue processEvent(ContractEventContext eventContext) { | |||||
if (LOGGER.isDebugEnabled()) { | |||||
LOGGER.debug("Start processing event[%s] of contract[%s]...", eventContext.getEvent(), address.toString()); | |||||
} | |||||
try { | |||||
return codeModule.call(new ContractExecution(eventContext)); | |||||
} catch (Exception ex) { | |||||
LOGGER.error(String.format("Error occurred while processing event[%s] of contract[%s]! --%s", | |||||
eventContext.getEvent(), address.toString(), ex.getMessage()), ex); | |||||
throw ex; | |||||
} finally { | |||||
if (LOGGER.isDebugEnabled()) { | |||||
LOGGER.debug("End processing event[%s] of contract[%s]. ", eventContext.getEvent(), address.toString()); | |||||
} | |||||
} | |||||
} | } | ||||
private Object resolveArgs(byte[] args, List<DataContract> dataContractList) { | |||||
if(args == null || args.length == 0){ | |||||
return null; | |||||
protected Object getContractInstance() { | |||||
try { | |||||
// 每一次调用都通过反射创建合约的实例; | |||||
return getContractDefinition().getMainClass().newInstance(); | |||||
} catch (InstantiationException | IllegalAccessException e) { | |||||
throw new IllegalStateException(e.getMessage(), e); | |||||
} | } | ||||
return ContractSerializeUtils.deserializeMethodParam(args,dataContractList); | |||||
} | } | ||||
class ContractExecution implements Runnable { | |||||
private CompletableFuture<String> contractReturn; | |||||
private class ContractExecution implements Callable<BytesValue> { | |||||
private ContractEventContext eventContext; | |||||
public ContractExecution(CompletableFuture<String> contractReturn) { | |||||
this.contractReturn = contractReturn; | |||||
public ContractExecution(ContractEventContext contractEventContext) { | |||||
this.eventContext = contractEventContext; | |||||
} | } | ||||
public void run() { | |||||
LOGGER.info("ContractThread execute()."); | |||||
try { | |||||
// 执行预处理; | |||||
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,耗时:" + (System.currentTimeMillis() - startTime)); | |||||
// Method eventMethod = this.getMethodByAnno(contractMainClassObj, contractEventContext.getEvent()); | |||||
startTime = System.currentTimeMillis(); | |||||
// 反序列化参数; | |||||
contractType = ContractType.resolve(myClass); | |||||
Method handleMethod = contractType.getHandleMethod(contractEventContext.getEvent()); | |||||
if (handleMethod == null){ | |||||
throw new IllegalDataException("don't get this method by it's @ContractEvent."); | |||||
} | |||||
Object args = resolveArgs(contractEventContext.getArgs(), | |||||
contractType.getDataContractMap().get(handleMethod)); | |||||
Object[] params = null; | |||||
if(args.getClass().isArray()){ | |||||
params = (Object[])args; | |||||
} | |||||
String contractReturn = ReflectionUtils.invokeMethod(handleMethod, contractMainClassObj, params).toString(); | |||||
LOGGER.info("合约执行,耗时:" + (System.currentTimeMillis() - startTime)); | |||||
Method mth2 = myClass.getMethod("postEvent"); | |||||
startTime = System.currentTimeMillis(); | |||||
ReflectionUtils.invokeMethod(mth2, contractMainClassObj); | |||||
LOGGER.info("postEvent,耗时:" + (System.currentTimeMillis() - startTime)); | |||||
// 填充return结果 | |||||
if (this.contractReturn != null) { | |||||
this.contractReturn.complete(contractReturn); | |||||
} | |||||
} catch (NoSuchMethodException e) { | |||||
throw new IllegalArgumentException(e.getMessage()); | |||||
} catch (Exception e) { | |||||
throw new IllegalDataException(e.getMessage()); | |||||
} | |||||
@Override | |||||
public BytesValue call() throws Exception { | |||||
return JavaContractCode.super.processEvent(eventContext); | |||||
} | } | ||||
} | } | ||||
@@ -8,7 +8,7 @@ import com.github.javaparser.ast.PackageDeclaration; | |||||
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; | import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; | ||||
import com.github.javaparser.ast.body.MethodDeclaration; | import com.github.javaparser.ast.body.MethodDeclaration; | ||||
import com.github.javaparser.ast.visitor.VoidVisitorAdapter; | import com.github.javaparser.ast.visitor.VoidVisitorAdapter; | ||||
import com.jd.blockchain.transaction.ContractType; | |||||
import com.jd.blockchain.contract.ContractType; | |||||
import com.jd.blockchain.utils.IllegalDataException; | import com.jd.blockchain.utils.IllegalDataException; | ||||
import org.apache.maven.plugin.AbstractMojo; | import org.apache.maven.plugin.AbstractMojo; | ||||
import org.apache.maven.plugin.MojoFailureException; | import org.apache.maven.plugin.MojoFailureException; | ||||
@@ -1,6 +1,5 @@ | |||||
package com.jd.blockchain.contract; | package com.jd.blockchain.contract; | ||||
import com.jd.blockchain.ledger.ContractBizContent; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
/** | /** | ||||
@@ -1,6 +1,5 @@ | |||||
package com.jd.blockchain.contract; | package com.jd.blockchain.contract; | ||||
import com.jd.blockchain.ledger.ContractBizContent; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | import com.jd.blockchain.ledger.KVDataEntry; | ||||
import com.jd.blockchain.ledger.KVDataObject; | import com.jd.blockchain.ledger.KVDataObject; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
@@ -34,11 +34,11 @@ | |||||
<plugin> | <plugin> | ||||
<artifactId>maven-assembly-plugin</artifactId> | <artifactId>maven-assembly-plugin</artifactId> | ||||
<configuration> | <configuration> | ||||
<finalName>contract</finalName> | |||||
<finalName>transfer</finalName> | |||||
<appendAssemblyId>false</appendAssemblyId> | <appendAssemblyId>false</appendAssemblyId> | ||||
<archive> | <archive> | ||||
<manifest> | <manifest> | ||||
<mainClass>com.jd.blockchain.contract.ReadContractImpl</mainClass> | |||||
<mainClass>com.jd.blockchain.contract.TransferContractImpl</mainClass> | |||||
</manifest> | </manifest> | ||||
</archive> | </archive> | ||||
<descriptorRefs> | <descriptorRefs> | ||||
@@ -6,5 +6,10 @@ public interface ReadContract { | |||||
@ContractEvent(name = "read-key") | @ContractEvent(name = "read-key") | ||||
String read(String address, String key); | String read(String address, String key); | ||||
@ContractEvent(name = "version-key") | |||||
Long readVersion(String address, String key); | |||||
int test(); | |||||
} | } | ||||
@@ -4,35 +4,26 @@ import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | import com.jd.blockchain.ledger.KVDataEntry; | ||||
@Contract | @Contract | ||||
public class ReadContractImpl implements EventProcessingAwire, ReadContract { | |||||
public class ReadContractImpl implements EventProcessingAware, ReadContract { | |||||
private ContractEventContext eventContext; | private ContractEventContext eventContext; | ||||
private HashDigest ledgerHash; | |||||
@Override | @Override | ||||
public void beforeEvent(ContractEventContext eventContext) { | public void beforeEvent(ContractEventContext eventContext) { | ||||
this.eventContext = eventContext; | this.eventContext = eventContext; | ||||
this.ledgerHash = eventContext.getCurrentLedgerHash(); | |||||
} | } | ||||
@Override | @Override | ||||
public void postEvent(ContractEventContext eventContext, ContractException error) { | |||||
} | |||||
@Override | |||||
public void postEvent(ContractException error) { | |||||
} | |||||
@Override | |||||
public void postEvent() { | |||||
public void postEvent(ContractEventContext eventContext, Exception error) { | |||||
} | } | ||||
@Override | @Override | ||||
@ContractEvent(name = "read-key") | @ContractEvent(name = "read-key") | ||||
public String read(String address, String key) { | public String read(String address, String key) { | ||||
HashDigest ledgerHash = eventContext.getCurrentLedgerHash(); | |||||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | ||||
if (kvDataEntries != null && kvDataEntries.length == 1) { | if (kvDataEntries != null && kvDataEntries.length == 1) { | ||||
@@ -40,4 +31,20 @@ public class ReadContractImpl implements EventProcessingAwire, ReadContract { | |||||
} | } | ||||
return null; | return null; | ||||
} | } | ||||
@Override | |||||
@ContractEvent(name = "version-key") | |||||
public Long readVersion(String address, String key) { | |||||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | |||||
if (kvDataEntries != null && kvDataEntries.length == 1) { | |||||
return kvDataEntries[0].getVersion(); | |||||
} | |||||
return -1L; | |||||
} | |||||
@Override | |||||
public int test() { | |||||
return 0; | |||||
} | |||||
} | } |
@@ -0,0 +1,17 @@ | |||||
package com.jd.blockchain.contract; | |||||
@Contract | |||||
public interface TransferContract { | |||||
@ContractEvent(name = "create") | |||||
String create(String address, String account, long money); | |||||
@ContractEvent(name = "transfer") | |||||
String transfer(String address, String from, String to, long money); | |||||
@ContractEvent(name = "read") | |||||
long read(String address, String account); | |||||
@ContractEvent(name = "readAll") | |||||
String readAll(String address, String account); | |||||
} |
@@ -0,0 +1,109 @@ | |||||
package com.jd.blockchain.contract; | |||||
import com.alibaba.fastjson.JSON; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | |||||
import com.jd.blockchain.ledger.KVDataVO; | |||||
import com.jd.blockchain.ledger.KVInfoVO; | |||||
public class TransferContractImpl implements EventProcessingAware, TransferContract { | |||||
private ContractEventContext eventContext; | |||||
private HashDigest ledgerHash; | |||||
@Override | |||||
public String create(String address, String account, long money) { | |||||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); | |||||
// 肯定有返回值,但若不存在则返回version=-1 | |||||
if (kvDataEntries != null && kvDataEntries.length > 0) { | |||||
long currVersion = kvDataEntries[0].getVersion(); | |||||
if (currVersion > -1) { | |||||
throw new IllegalStateException(String.format("%s -> %s already have created !!!", address, account)); | |||||
} | |||||
eventContext.getLedger().dataAccount(address).setInt64(account, money, -1L); | |||||
} else { | |||||
throw new IllegalStateException(String.format("Ledger[%s] inner Error !!!", ledgerHash.toBase58())); | |||||
} | |||||
return String.format("DataAccountAddress[%s] -> Create(By Contract Operation) Account = %s and Money = %s Success!!! \r\n", | |||||
address, account, money); | |||||
} | |||||
@Override | |||||
public String transfer(String address, String from, String to, long money) { | |||||
// 首先查询余额 | |||||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, from, to); | |||||
if (kvDataEntries == null || kvDataEntries.length != 2) { | |||||
throw new IllegalStateException(String.format("%s -> %s - %s may be not created !!!", address, from, to)); | |||||
} else { | |||||
// 判断from账号中钱数量是否足够 | |||||
long fromMoney = 0L, toMoney = 0L, fromVersion = 0L, toVersion = 0L; | |||||
for (KVDataEntry kvDataEntry : kvDataEntries) { | |||||
if (kvDataEntry.getKey().equals(from)) { | |||||
fromMoney = (long) kvDataEntry.getValue(); | |||||
fromVersion = kvDataEntry.getVersion(); | |||||
} else { | |||||
toMoney = (long) kvDataEntry.getValue(); | |||||
toVersion = kvDataEntry.getVersion(); | |||||
} | |||||
} | |||||
if (fromMoney < money) { | |||||
throw new IllegalStateException(String.format("%s -> %s not have enough money !!!", address, from)); | |||||
} | |||||
long fromNewMoney = fromMoney - money; | |||||
long toNewMoney = toMoney + money; | |||||
// 重新设置 | |||||
eventContext.getLedger().dataAccount(address).setInt64(from, fromNewMoney, fromVersion); | |||||
eventContext.getLedger().dataAccount(address).setInt64(to, toNewMoney, toVersion); | |||||
} | |||||
return String.format("DataAccountAddress[%s] transfer from [%s] to [%s] and [money = %s] Success !!!", address, from, to, money); | |||||
} | |||||
@Override | |||||
public long read(String address, String account) { | |||||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); | |||||
if (kvDataEntries == null || kvDataEntries.length == 0) { | |||||
return -1; | |||||
} | |||||
return (long)kvDataEntries[0].getValue(); | |||||
} | |||||
@Override | |||||
public String readAll(String address, String account) { | |||||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); | |||||
// 获取最新的版本号 | |||||
if (kvDataEntries == null || kvDataEntries.length == 0) { | |||||
return ""; | |||||
} | |||||
long newestVersion = kvDataEntries[0].getVersion(); | |||||
if (newestVersion == -1) { | |||||
return ""; | |||||
} | |||||
KVDataVO[] kvDataVOS = new KVDataVO[1]; | |||||
long[] versions = new long[(int)newestVersion + 1]; | |||||
for (int i = 0; i < versions.length; i++) { | |||||
versions[i] = i; | |||||
} | |||||
KVDataVO kvDataVO = new KVDataVO(account, versions); | |||||
kvDataVOS[0] = kvDataVO; | |||||
KVInfoVO kvInfoVO = new KVInfoVO(kvDataVOS); | |||||
KVDataEntry[] allEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, kvInfoVO); | |||||
return JSON.toJSONString(allEntries); | |||||
} | |||||
@Override | |||||
public void beforeEvent(ContractEventContext eventContext) { | |||||
this.eventContext = eventContext; | |||||
this.ledgerHash = eventContext.getCurrentLedgerHash(); | |||||
} | |||||
@Override | |||||
public void postEvent(ContractEventContext eventContext, Exception error) { | |||||
} | |||||
} |
@@ -7,15 +7,21 @@ public final class ClassicAlgorithm { | |||||
public static final CryptoAlgorithm ED25519 = CryptoAlgorithmDefinition.defineSignature("ED25519", false, | public static final CryptoAlgorithm ED25519 = CryptoAlgorithmDefinition.defineSignature("ED25519", false, | ||||
(byte) 21); | (byte) 21); | ||||
public static final CryptoAlgorithm ECDSA = CryptoAlgorithmDefinition.defineSignature("ECDSA", false, (byte) 22); | |||||
public static final CryptoAlgorithm RSA = CryptoAlgorithmDefinition.defineSignature("RSA", true, (byte) 23); | |||||
public static final CryptoAlgorithm ECDSA = CryptoAlgorithmDefinition.defineSignature("ECDSA", false, | |||||
(byte) 22); | |||||
public static final CryptoAlgorithm SHA256 = CryptoAlgorithmDefinition.defineHash("SHA256", (byte) 24); | |||||
public static final CryptoAlgorithm RSA = CryptoAlgorithmDefinition.defineSignature("RSA", true, | |||||
(byte) 23); | |||||
public static final CryptoAlgorithm RIPEMD160 = CryptoAlgorithmDefinition.defineHash("RIPEMD160", (byte) 25); | |||||
public static final CryptoAlgorithm SHA256 = CryptoAlgorithmDefinition.defineHash("SHA256", | |||||
(byte) 24); | |||||
public static final CryptoAlgorithm AES = CryptoAlgorithmDefinition.defineSymmetricEncryption("AES", (byte) 26); | |||||
public static final CryptoAlgorithm RIPEMD160 = CryptoAlgorithmDefinition.defineHash("RIPEMD160", | |||||
(byte) 25); | |||||
public static final CryptoAlgorithm AES = CryptoAlgorithmDefinition.defineSymmetricEncryption("AES", | |||||
(byte) 26); | |||||
public static final CryptoAlgorithm JVM_SECURE_RANDOM = CryptoAlgorithmDefinition.defineRandom("JVM-SECURE-RANDOM", | public static final CryptoAlgorithm JVM_SECURE_RANDOM = CryptoAlgorithmDefinition.defineRandom("JVM-SECURE-RANDOM", | ||||
(byte) 27); | (byte) 27); | ||||
@@ -0,0 +1,18 @@ | |||||
package test.com.jd.blockchain.crypto.service.classic; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.codec.HexUtils; | |||||
import org.junit.Assert; | |||||
import org.junit.Test; | |||||
public class EncodeTest { | |||||
@Test | |||||
public void test() { | |||||
String pubKey = "7VeRLdGtSz1Y91gjLTqEdnkotzUfaAqdap3xw6fQ1yKHkvVq"; | |||||
Bytes bytes = Bytes.fromBase58(pubKey); | |||||
String hexString = HexUtils.encode(bytes.toBytes()); | |||||
String code = hexString.substring(2, 4); | |||||
Assert.assertEquals(code, "15"); // 15为十六进制,对应十进制为21(ED25519) | |||||
} | |||||
} |
@@ -45,7 +45,7 @@ | |||||
<dependency> | <dependency> | ||||
<groupId>com.jd.blockchain</groupId> | <groupId>com.jd.blockchain</groupId> | ||||
<artifactId>browser</artifactId> | |||||
<artifactId>explorer</artifactId> | |||||
</dependency> | </dependency> | ||||
<dependency> | <dependency> | ||||
@@ -78,6 +78,23 @@ | |||||
<version>${commons-io.version}</version> | <version>${commons-io.version}</version> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>org.bitbucket.mstrobel</groupId> | |||||
<artifactId>procyon-core</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.bitbucket.mstrobel</groupId> | |||||
<artifactId>procyon-expressions</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.bitbucket.mstrobel</groupId> | |||||
<artifactId>procyon-reflection</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.bitbucket.mstrobel</groupId> | |||||
<artifactId>procyon-compilertools</artifactId> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>org.springframework.boot</groupId> | <groupId>org.springframework.boot</groupId> | ||||
<artifactId>spring-boot-starter-web</artifactId> | <artifactId>spring-boot-starter-web</artifactId> | ||||
@@ -0,0 +1,249 @@ | |||||
package com.jd.blockchain.gateway.decompiler.loads; | |||||
import com.strobel.assembler.ir.ConstantPool; | |||||
import com.strobel.assembler.metadata.Buffer; | |||||
import com.strobel.assembler.metadata.ClasspathTypeLoader; | |||||
import com.strobel.assembler.metadata.ITypeLoader; | |||||
import com.strobel.core.StringUtilities; | |||||
import com.strobel.core.VerifyArgument; | |||||
import java.io.ByteArrayInputStream; | |||||
import java.util.LinkedHashMap; | |||||
import java.util.LinkedHashSet; | |||||
import java.util.Map; | |||||
import java.util.logging.Level; | |||||
import java.util.logging.Logger; | |||||
public class BytesTypeLoader implements ITypeLoader { | |||||
private final static Logger LOG = Logger.getLogger(BytesTypeLoader.class.getSimpleName()); | |||||
private final ITypeLoader defaultTypeLoader; | |||||
private final Map<String, LinkedHashSet<byte[]>> packageLocations; | |||||
private final Map<String, byte[]> knownBytes; | |||||
private String name; | |||||
public BytesTypeLoader(byte[] bytes) { | |||||
this(new ClasspathTypeLoader(), bytes); | |||||
} | |||||
public BytesTypeLoader(final ITypeLoader defaultTypeLoader, byte[] bytes) { | |||||
this.defaultTypeLoader = VerifyArgument.notNull(defaultTypeLoader, "defaultTypeLoader"); | |||||
this.packageLocations = new LinkedHashMap<>(); | |||||
this.knownBytes = new LinkedHashMap<>(); | |||||
Buffer innerNameBuffer = new Buffer(); | |||||
if (tryLoadTypeFromBytes(bytes, innerNameBuffer)) { | |||||
this.name = getInternalNameFromClassFile(innerNameBuffer); | |||||
this.knownBytes.put(this.name, bytes); | |||||
} else { | |||||
throw new IllegalStateException("Input Class Bytes Exception !!!"); | |||||
} | |||||
} | |||||
@Override | |||||
public boolean tryLoadType(final String typeNameOrPath, final Buffer buffer) { | |||||
VerifyArgument.notNull(typeNameOrPath, "typeNameOrPath"); | |||||
VerifyArgument.notNull(buffer, "buffer"); | |||||
if (LOG.isLoggable(Level.FINE)) { | |||||
LOG.fine("Attempting to load type: " + typeNameOrPath + "..."); | |||||
} | |||||
final boolean hasExtension = StringUtilities.endsWithIgnoreCase(typeNameOrPath, ".class"); | |||||
if (hasExtension) { | |||||
return false; | |||||
} | |||||
String internalName = typeNameOrPath; | |||||
if (tryLoadTypeFromName(internalName, buffer)) { | |||||
return true; | |||||
} | |||||
for (int lastDelimiter = internalName.lastIndexOf('/'); | |||||
lastDelimiter != -1; | |||||
lastDelimiter = internalName.lastIndexOf('/')) { | |||||
internalName = internalName.substring(0, lastDelimiter) + "$" + | |||||
internalName.substring(lastDelimiter + 1); | |||||
if (tryLoadTypeFromName(internalName, buffer)) { | |||||
return true; | |||||
} | |||||
} | |||||
if (LOG.isLoggable(Level.FINER)) { | |||||
LOG.finer("Failed to load type: " + typeNameOrPath + "."); | |||||
} | |||||
return false; | |||||
} | |||||
private boolean tryLoadTypeFromName(final String internalName, final Buffer buffer) { | |||||
if (tryLoadFromKnownLocation(internalName, buffer)) { | |||||
return true; | |||||
} | |||||
if (defaultTypeLoader.tryLoadType(internalName, buffer)) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
private boolean tryLoadFromKnownLocation(final String internalName, final Buffer buffer) { | |||||
final byte[] knownFile = knownBytes.get(internalName); | |||||
if (tryLoadBytes(knownFile, buffer)) { | |||||
return true; | |||||
} | |||||
final int packageEnd = internalName.lastIndexOf('/'); | |||||
String head; | |||||
String tail; | |||||
if (packageEnd < 0 || packageEnd >= internalName.length()) { | |||||
head = StringUtilities.EMPTY; | |||||
tail = internalName; | |||||
} | |||||
else { | |||||
head = internalName.substring(0, packageEnd); | |||||
tail = internalName.substring(packageEnd + 1); | |||||
} | |||||
while (true) { | |||||
final LinkedHashSet<byte[]> directories = packageLocations.get(head); | |||||
if (directories != null) { | |||||
for (final byte[] directory : directories) { | |||||
if (tryLoadBytes(internalName, directory, buffer, true)) { | |||||
return true; | |||||
} | |||||
} | |||||
} | |||||
final int split = head.lastIndexOf('/'); | |||||
if (split <= 0) { | |||||
break; | |||||
} | |||||
tail = head.substring(split + 1) + '/' + tail; | |||||
head = head.substring(0, split); | |||||
} | |||||
return false; | |||||
} | |||||
private boolean tryLoadBytes(final byte[] bytes, final Buffer buffer) { | |||||
if (bytes == null || bytes.length == 0) { | |||||
return false; | |||||
} | |||||
int length = bytes.length; | |||||
buffer.position(0); | |||||
buffer.reset(length); | |||||
new ByteArrayInputStream(bytes).read(buffer.array(), 0, length); | |||||
buffer.position(0); | |||||
return true; | |||||
} | |||||
private boolean tryLoadBytes(final String internalName, final byte[] bytes, final Buffer buffer, final boolean trustName) { | |||||
if (!tryLoadBytes(bytes, buffer)) { | |||||
return false; | |||||
} | |||||
final String actualName = getInternalNameFromClassFile(buffer); | |||||
final String name = trustName ? (internalName != null ? internalName : actualName) | |||||
: actualName; | |||||
if (name == null) { | |||||
return false; | |||||
} | |||||
final boolean nameMatches = StringUtilities.equals(actualName, internalName); | |||||
final boolean result = internalName == null || nameMatches; | |||||
if (result) { | |||||
final int packageEnd = name.lastIndexOf('/'); | |||||
final String packageName; | |||||
if (packageEnd < 0 || packageEnd >= name.length()) { | |||||
packageName = StringUtilities.EMPTY; | |||||
} | |||||
else { | |||||
packageName = name.substring(0, packageEnd); | |||||
} | |||||
registerKnownPath(packageName, bytes); | |||||
knownBytes.put(actualName, bytes); | |||||
} | |||||
else { | |||||
buffer.reset(0); | |||||
} | |||||
return result; | |||||
} | |||||
private void registerKnownPath(final String packageName, final byte[] directory) { | |||||
if (directory == null || directory.length == 0) { | |||||
return; | |||||
} | |||||
LinkedHashSet<byte[]> directories = packageLocations.get(packageName); | |||||
if (directories == null) { | |||||
packageLocations.put(packageName, directories = new LinkedHashSet<>()); | |||||
} | |||||
if (!directories.add(directory)) { | |||||
return; | |||||
} | |||||
} | |||||
private static String getInternalNameFromClassFile(final Buffer b) { | |||||
final long magic = b.readInt() & 0xFFFFFFFFL; | |||||
if (magic != 0xCAFEBABEL) { | |||||
return null; | |||||
} | |||||
b.readUnsignedShort(); // minor version | |||||
b.readUnsignedShort(); // major version | |||||
final ConstantPool constantPool = ConstantPool.read(b); | |||||
b.readUnsignedShort(); // access flags | |||||
final ConstantPool.TypeInfoEntry thisClass = constantPool.getEntry(b.readUnsignedShort()); | |||||
b.position(0); | |||||
return thisClass.getName(); | |||||
} | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
private boolean tryLoadTypeFromBytes(byte[] bytes, Buffer buffer) { | |||||
if (bytes == null || bytes.length == 0 || buffer == null) { | |||||
return false; | |||||
} | |||||
int length = bytes.length; | |||||
buffer.position(0); | |||||
buffer.reset(length); | |||||
new ByteArrayInputStream(bytes).read(buffer.array(), 0, length); | |||||
buffer.position(0); | |||||
return true; | |||||
} | |||||
} |
@@ -0,0 +1,218 @@ | |||||
package com.jd.blockchain.gateway.decompiler.utils; | |||||
import com.jd.blockchain.gateway.decompiler.loads.BytesTypeLoader; | |||||
import com.strobel.assembler.metadata.JarTypeLoader; | |||||
import com.strobel.decompiler.Decompiler; | |||||
import com.strobel.decompiler.DecompilerSettings; | |||||
import com.strobel.decompiler.PlainTextOutput; | |||||
import org.apache.commons.io.FileUtils; | |||||
import org.apache.commons.io.IOUtils; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.StringWriter; | |||||
import java.net.URL; | |||||
import java.util.Arrays; | |||||
import java.util.List; | |||||
import java.util.concurrent.atomic.AtomicLong; | |||||
import java.util.jar.JarFile; | |||||
public class DecompilerUtils { | |||||
public static final AtomicLong SAVE_INDEX = new AtomicLong(); | |||||
public static final String MANIFEST_MF = "/META-INF/MANIFEST.MF"; | |||||
public static final String MAIN_CLASS = "Main-Class"; | |||||
public static String SAVE_DIR = null; | |||||
static { | |||||
init(); | |||||
} | |||||
private static void init() { | |||||
try { | |||||
URL url = DecompilerUtils.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); | |||||
} | |||||
File file = new File(currPath); | |||||
String homeDir = file.getParent(); | |||||
SAVE_DIR = homeDir + File.separator + "temp"; | |||||
File dir = new File(SAVE_DIR); | |||||
if (!dir.exists()) { | |||||
dir.mkdir(); | |||||
} | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
public static String decompile(String classPath) { | |||||
String decompileJava; | |||||
try (StringWriter stringWriter = new StringWriter()) { | |||||
final DecompilerSettings settings = DecompilerSettings.javaDefaults(); | |||||
Decompiler.decompile( | |||||
classPath, | |||||
new PlainTextOutput(stringWriter), | |||||
settings | |||||
); | |||||
decompileJava = stringWriter.toString(); | |||||
} catch (final Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
return decompileJava; | |||||
} | |||||
public static List<String> readManifest2Array(final String jarFilePath, final String charSet) { | |||||
String manifest = readManifest(jarFilePath, charSet); | |||||
String[] manifests = manifest.split("\r\n"); | |||||
return Arrays.asList(manifests); | |||||
} | |||||
public static String readManifest(final String jarFilePath, final String charSet) { | |||||
return decompileJarFile(jarFilePath, MANIFEST_MF, false, charSet); | |||||
} | |||||
public static String decompileMainClassFromBytes(byte[] bytes) { | |||||
try { | |||||
String jarFile = writeBytes(bytes, SAVE_DIR, "jar"); | |||||
String decompileJava = decompileMainClassFromJarFile(jarFile); | |||||
// 然后删除jarFile文件 | |||||
FileUtils.forceDelete(new File(jarFile)); | |||||
return decompileJava; | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
public static String decompileMainClassFromJarFile(final String jarFilePath) { | |||||
// 首先获取Main-Class | |||||
List<String> manifests = readManifest2Array(jarFilePath, null); | |||||
if (manifests == null || manifests.size() == 0) { | |||||
throw new IllegalStateException("MANIFEST.MF not Exist or is Empty !!!"); | |||||
} else { | |||||
String mainClass = null; | |||||
for (String s : manifests) { | |||||
String inner = s.trim().replaceAll(" ", ""); | |||||
if (inner.startsWith(MAIN_CLASS)) { | |||||
mainClass = inner.split(":")[1]; | |||||
break; | |||||
} | |||||
} | |||||
if (mainClass == null || mainClass.length() == 0) { | |||||
throw new IllegalStateException("MANIFEST.MF has not Main-Class !!!"); | |||||
} | |||||
// 然后读取MainClass中的内容并进行反编译 | |||||
String classPath = mainClass.replaceAll("\\.", "/"); | |||||
return decompileJarFile(jarFilePath, classPath, true, null); | |||||
} | |||||
} | |||||
public static String decompileJarFile(final String jarFilePath, final String source, final boolean isClass, final String charSet) { | |||||
// 对于Class文件和非Class文件处理方式不同 | |||||
if (!isClass) { | |||||
// 非Class文件不需要编译,直接从文件中读取即可 | |||||
String innerSource = source; | |||||
if (!innerSource.startsWith("/")) { | |||||
innerSource = "/" + innerSource; | |||||
} | |||||
try { | |||||
URL jarUrl = new URL("jar:file:" + jarFilePath + "!" + innerSource); | |||||
InputStream inputStream = jarUrl.openStream(); | |||||
byte[] bytes = IOUtils.toByteArray(inputStream); | |||||
if (charSet == null) { | |||||
return new String(bytes); | |||||
} | |||||
return new String(bytes, charSet); | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} else { | |||||
String decompileJava; | |||||
try (StringWriter stringWriter = new StringWriter()) { | |||||
JarFile jarFile = new JarFile(jarFilePath); | |||||
JarTypeLoader jarTypeLoader = new JarTypeLoader(jarFile); | |||||
final DecompilerSettings settings = DecompilerSettings.javaDefaults(); | |||||
settings.setTypeLoader(jarTypeLoader); | |||||
Decompiler.decompile( | |||||
source, | |||||
new PlainTextOutput(stringWriter), | |||||
settings | |||||
); | |||||
decompileJava = stringWriter.toString(); | |||||
} catch (final Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
return decompileJava; | |||||
} | |||||
} | |||||
public static String decompile(byte[] classBytes) { | |||||
String decompileJava; | |||||
try (StringWriter stringWriter = new StringWriter()) { | |||||
BytesTypeLoader bytesTypeLoader = new BytesTypeLoader(classBytes); | |||||
String name = bytesTypeLoader.getName(); | |||||
final DecompilerSettings settings = DecompilerSettings.javaDefaults(); | |||||
settings.setTypeLoader(bytesTypeLoader); | |||||
Decompiler.decompile( | |||||
name, | |||||
new PlainTextOutput(stringWriter), | |||||
settings | |||||
); | |||||
decompileJava = stringWriter.toString(); | |||||
} catch (final Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
return decompileJava; | |||||
} | |||||
public static String decompile(InputStream in) { | |||||
try { | |||||
return decompile(IOUtils.toByteArray(in)); | |||||
} catch (final Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
public static byte[] read2Bytes(String filePath) throws IOException { | |||||
return FileUtils.readFileToByteArray(new File(filePath)); | |||||
} | |||||
public static String writeBytes(byte[] bytes, String directory, String suffix) throws IOException { | |||||
String saveFileName = System.currentTimeMillis() + "-" + SAVE_INDEX.incrementAndGet() + "." + suffix; | |||||
File saveFile = new File(directory + File.separator + saveFileName); | |||||
FileUtils.writeByteArrayToFile(saveFile, bytes); | |||||
return saveFile.getPath(); | |||||
} | |||||
} |
@@ -2,7 +2,9 @@ package com.jd.blockchain.gateway.service; | |||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.ParticipantNode; | import com.jd.blockchain.ledger.ParticipantNode; | ||||
import com.jd.blockchain.sdk.ContractSettings; | |||||
import com.jd.blockchain.sdk.LedgerInitSettings; | import com.jd.blockchain.sdk.LedgerInitSettings; | ||||
import com.jd.blockchain.utils.Bytes; | |||||
/** | /** | ||||
* queryService only for gateway; | * queryService only for gateway; | ||||
@@ -34,4 +36,15 @@ public interface GatewayQueryService { | |||||
* @return | * @return | ||||
*/ | */ | ||||
LedgerInitSettings getLedgerInitSettings(HashDigest ledgerHash); | LedgerInitSettings getLedgerInitSettings(HashDigest ledgerHash); | ||||
/** | |||||
* 获取账本指定合约信息 | |||||
* | |||||
* @param ledgerHash | |||||
* 账本Hash | |||||
* @param address | |||||
* 合约地址 | |||||
* @return | |||||
*/ | |||||
ContractSettings getContractSettings(HashDigest ledgerHash, String address); | |||||
} | } |
@@ -7,8 +7,11 @@ import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider; | |||||
import com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider; | import com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.gateway.PeerService; | import com.jd.blockchain.gateway.PeerService; | ||||
import com.jd.blockchain.gateway.decompiler.utils.DecompilerUtils; | |||||
import com.jd.blockchain.ledger.ContractInfo; | |||||
import com.jd.blockchain.ledger.LedgerMetadata; | import com.jd.blockchain.ledger.LedgerMetadata; | ||||
import com.jd.blockchain.ledger.ParticipantNode; | import com.jd.blockchain.ledger.ParticipantNode; | ||||
import com.jd.blockchain.sdk.ContractSettings; | |||||
import com.jd.blockchain.sdk.LedgerInitSettings; | import com.jd.blockchain.sdk.LedgerInitSettings; | ||||
import com.jd.blockchain.utils.QueryUtil; | import com.jd.blockchain.utils.QueryUtil; | ||||
import com.jd.blockchain.utils.codec.HexUtils; | import com.jd.blockchain.utils.codec.HexUtils; | ||||
@@ -53,6 +56,21 @@ public class GatewayQueryServiceHandler implements GatewayQueryService { | |||||
return initLedgerInitSettings(participantNodes, ledgerMetadata); | return initLedgerInitSettings(participantNodes, ledgerMetadata); | ||||
} | } | ||||
@Override | |||||
public ContractSettings getContractSettings(HashDigest ledgerHash, String address) { | |||||
ContractInfo contractInfo = peerService.getQueryService().getContract(ledgerHash, address); | |||||
return contractSettings(contractInfo); | |||||
} | |||||
private ContractSettings contractSettings(ContractInfo contractInfo) { | |||||
ContractSettings contractSettings = new ContractSettings(contractInfo.getAddress(), contractInfo.getPubKey(), contractInfo.getRootHash()); | |||||
byte[] chainCodeBytes = contractInfo.getChainCode(); | |||||
// 将反编译chainCode | |||||
String mainClassJava = DecompilerUtils.decompileMainClassFromBytes(chainCodeBytes); | |||||
contractSettings.setChainCode(mainClassJava); | |||||
return contractSettings; | |||||
} | |||||
/** | /** | ||||
* 初始化账本配置 | * 初始化账本配置 | ||||
* | * | ||||
@@ -8,6 +8,7 @@ import com.jd.blockchain.gateway.service.DataRetrievalService; | |||||
import com.jd.blockchain.gateway.service.GatewayQueryService; | import com.jd.blockchain.gateway.service.GatewayQueryService; | ||||
import com.jd.blockchain.ledger.*; | import com.jd.blockchain.ledger.*; | ||||
import com.jd.blockchain.sdk.BlockchainExtendQueryService; | import com.jd.blockchain.sdk.BlockchainExtendQueryService; | ||||
import com.jd.blockchain.sdk.ContractSettings; | |||||
import com.jd.blockchain.sdk.LedgerInitSettings; | import com.jd.blockchain.sdk.LedgerInitSettings; | ||||
import com.jd.blockchain.tools.keygen.KeyGenCommand; | import com.jd.blockchain.tools.keygen.KeyGenCommand; | ||||
import com.jd.blockchain.utils.BaseConstant; | import com.jd.blockchain.utils.BaseConstant; | ||||
@@ -23,6 +24,7 @@ import java.util.List; | |||||
@RestController | @RestController | ||||
@RequestMapping(path = "/") | @RequestMapping(path = "/") | ||||
public class BlockBrowserController implements BlockchainExtendQueryService { | public class BlockBrowserController implements BlockchainExtendQueryService { | ||||
private static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(BlockBrowserController.class); | private static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(BlockBrowserController.class); | ||||
@Autowired | @Autowired | ||||
@@ -55,7 +57,7 @@ public class BlockBrowserController implements BlockchainExtendQueryService { | |||||
// @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants") | // @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants") | ||||
@Override | @Override | ||||
public ParticipantNode[] getConsensusParticipants(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { | |||||
public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash) { | |||||
return peerService.getQueryService().getConsensusParticipants(ledgerHash); | return peerService.getQueryService().getConsensusParticipants(ledgerHash); | ||||
} | } | ||||
@@ -263,10 +265,15 @@ public class BlockBrowserController implements BlockchainExtendQueryService { | |||||
} | } | ||||
@RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts/address/{address}") | @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts/address/{address}") | ||||
public ContractSettings getContractSettings(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, | |||||
@PathVariable(name = "address") String address) { | |||||
return gatewayQueryService.getContractSettings(ledgerHash, address); | |||||
} | |||||
// @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts/address/{address}") | |||||
@Override | @Override | ||||
public AccountHeader getContract(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, | |||||
@PathVariable(name = "address") String address) { | |||||
return peerService.getQueryService().getContract(ledgerHash, address); | |||||
public ContractInfo getContract(HashDigest ledgerHash, String address) { | |||||
return peerService.getQueryService().getContract(ledgerHash, address); | |||||
} | } | ||||
@RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/latest") | @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/latest") | ||||
@@ -450,7 +457,8 @@ public class BlockBrowserController implements BlockchainExtendQueryService { | |||||
} | } | ||||
@RequestMapping(method = RequestMethod.GET, value = "ledgers/{ledgerHash}/**/search") | @RequestMapping(method = RequestMethod.GET, value = "ledgers/{ledgerHash}/**/search") | ||||
public Object dataRetrieval(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,HttpServletRequest request) { | |||||
public Object dataRetrieval(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, | |||||
HttpServletRequest request) { | |||||
String result; | String result; | ||||
if (dataRetrievalUrl == null || dataRetrievalUrl.length() <= 0) { | if (dataRetrievalUrl == null || dataRetrievalUrl.length() <= 0) { | ||||
result = "{'message':'OK','data':'" + "data.retrieval.url is empty" + "'}"; | result = "{'message':'OK','data':'" + "data.retrieval.url is empty" + "'}"; | ||||
@@ -162,8 +162,10 @@ public class AccountSet implements Transactional, MerkleProvable { | |||||
* | * | ||||
* 只有最新版本的账户才能可写的,其它都是只读; | * 只有最新版本的账户才能可写的,其它都是只读; | ||||
* | * | ||||
* @param address 账户地址; | |||||
* @param version 账户版本;如果指定为 -1,则返回最新版本; | |||||
* @param address | |||||
* 账户地址; | |||||
* @param version | |||||
* 账户版本;如果指定为 -1,则返回最新版本; | |||||
* @return | * @return | ||||
*/ | */ | ||||
public BaseAccount getAccount(Bytes address, long version) { | public BaseAccount getAccount(Bytes address, long version) { | ||||
@@ -4,10 +4,11 @@ import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.crypto.PubKey; | import com.jd.blockchain.crypto.PubKey; | ||||
import com.jd.blockchain.ledger.AccountHeader; | import com.jd.blockchain.ledger.AccountHeader; | ||||
import com.jd.blockchain.ledger.BytesValue; | import com.jd.blockchain.ledger.BytesValue; | ||||
import com.jd.blockchain.ledger.BytesValueEntry; | |||||
import com.jd.blockchain.ledger.BytesData; | |||||
import com.jd.blockchain.ledger.ContractInfo; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
public class ContractAccount implements AccountHeader { | |||||
public class ContractAccount implements ContractInfo { | |||||
private static final Bytes CONTRACT_INFO_PREFIX = Bytes.fromString("INFO" + LedgerConsts.KEY_SEPERATOR); | private static final Bytes CONTRACT_INFO_PREFIX = Bytes.fromString("INFO" + LedgerConsts.KEY_SEPERATOR); | ||||
@@ -43,7 +44,7 @@ public class ContractAccount implements AccountHeader { | |||||
} | } | ||||
public long setChaincode(byte[] chaincode, long version) { | public long setChaincode(byte[] chaincode, long version) { | ||||
BytesValue bytesValue = BytesValueEntry.fromBytes(chaincode); | |||||
BytesValue bytesValue = BytesData.fromBytes(chaincode); | |||||
return accBase.setBytes(CHAIN_CODE_KEY, bytesValue, version); | return accBase.setBytes(CHAIN_CODE_KEY, bytesValue, version); | ||||
} | } | ||||
@@ -60,18 +61,18 @@ public class ContractAccount implements AccountHeader { | |||||
} | } | ||||
public long setProperty(Bytes key, String value, long version) { | public long setProperty(Bytes key, String value, long version) { | ||||
BytesValue bytesValue = BytesValueEntry.fromText(value); | |||||
BytesValue bytesValue = BytesData.fromText(value); | |||||
return accBase.setBytes(encodePropertyKey(key), bytesValue, version); | return accBase.setBytes(encodePropertyKey(key), bytesValue, version); | ||||
} | } | ||||
public String getProperty(Bytes key) { | public String getProperty(Bytes key) { | ||||
BytesValue bytesValue = accBase.getBytes(encodePropertyKey(key)); | BytesValue bytesValue = accBase.getBytes(encodePropertyKey(key)); | ||||
return BytesValueEntry.toText(bytesValue); | |||||
return BytesData.toText(bytesValue); | |||||
} | } | ||||
public String getProperty(Bytes key, long version) { | public String getProperty(Bytes key, long version) { | ||||
BytesValue bytesValue = accBase.getBytes(encodePropertyKey(key), version); | BytesValue bytesValue = accBase.getBytes(encodePropertyKey(key), version); | ||||
return BytesValueEntry.toText(bytesValue); | |||||
return BytesData.toText(bytesValue); | |||||
} | } | ||||
private Bytes encodePropertyKey(Bytes key) { | private Bytes encodePropertyKey(Bytes key) { | ||||
@@ -6,7 +6,7 @@ import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.crypto.PubKey; | import com.jd.blockchain.crypto.PubKey; | ||||
import com.jd.blockchain.ledger.AccountHeader; | import com.jd.blockchain.ledger.AccountHeader; | ||||
import com.jd.blockchain.ledger.BytesValue; | import com.jd.blockchain.ledger.BytesValue; | ||||
import com.jd.blockchain.ledger.BytesValueEntry; | |||||
import com.jd.blockchain.ledger.BytesData; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | import com.jd.blockchain.ledger.KVDataEntry; | ||||
import com.jd.blockchain.ledger.KVDataObject; | import com.jd.blockchain.ledger.KVDataObject; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
@@ -49,15 +49,15 @@ public class DataAccount implements AccountHeader, MerkleProvable { | |||||
public long setBytes(Bytes key, String value, long version) { | public long setBytes(Bytes key, String value, long version) { | ||||
BytesValue bytesValue = BytesValueEntry.fromText(value); | |||||
BytesValue bytesValue = BytesData.fromText(value); | |||||
return baseAccount.setBytes(key, bytesValue, version); | return baseAccount.setBytes(key, bytesValue, version); | ||||
} | } | ||||
public long setBytes(Bytes key, byte[] value, long version) { | public long setBytes(Bytes key, byte[] value, long version) { | ||||
BytesValue bytesValue = BytesValueEntry.fromBytes(value); | |||||
BytesValue bytesValue = BytesData.fromBytes(value); | |||||
return baseAccount.setBytes(key, bytesValue, version); | return baseAccount.setBytes(key, bytesValue, version); | ||||
} | } | ||||
/** | /** | ||||
* Return the latest version entry associated the specified key; If the key | * Return the latest version entry associated the specified key; If the key | ||||
* doesn't exist, then return -1; | * doesn't exist, then return -1; | ||||
@@ -1,8 +1,8 @@ | |||||
package com.jd.blockchain.ledger.core; | package com.jd.blockchain.ledger.core; | ||||
import com.jd.blockchain.ledger.TransactionState; | |||||
import com.jd.blockchain.ledger.LedgerTransaction; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | |||||
import com.jd.blockchain.ledger.*; | |||||
import java.util.List; | |||||
/** | /** | ||||
* 事务上下文; | * 事务上下文; | ||||
@@ -28,22 +28,43 @@ public interface LedgerTransactionContext { | |||||
/** | /** | ||||
* 提交对账本数据的修改,以指定的交易状态提交交易; | * 提交对账本数据的修改,以指定的交易状态提交交易; | ||||
* | |||||
* | |||||
* @param txResult | * @param txResult | ||||
* | |||||
* @return | * @return | ||||
*/ | */ | ||||
LedgerTransaction commit(TransactionState txResult); | LedgerTransaction commit(TransactionState txResult); | ||||
/** | /** | ||||
* 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | |||||
* 提交对账本数据的修改,以指定的交易状态提交交易; | |||||
* | * | ||||
* @param txResult | |||||
* @param operationResults | |||||
* | |||||
* @return | |||||
*/ | |||||
LedgerTransaction commit(TransactionState txResult, List<OperationResult> operationResults); | |||||
/** | |||||
* 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | |||||
* | |||||
* 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | * 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | ||||
* | |||||
* | |||||
* @param txResult | * @param txResult | ||||
* @return | * @return | ||||
*/ | */ | ||||
LedgerTransaction discardAndCommit(TransactionState txResult); | LedgerTransaction discardAndCommit(TransactionState txResult); | ||||
/** | |||||
* 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | |||||
* | |||||
* 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState, List)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||||
* | |||||
* @param txResult | |||||
* @return | |||||
*/ | |||||
LedgerTransaction discardAndCommit(TransactionState txResult, List<OperationResult> operationResults); | |||||
/** | /** | ||||
* 回滚事务,抛弃本次事务的所有数据更新; | * 回滚事务,抛弃本次事务的所有数据更新; | ||||
*/ | */ | ||||
@@ -1,8 +1,10 @@ | |||||
package com.jd.blockchain.ledger.core; | package com.jd.blockchain.ledger.core; | ||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
public interface OperationHandle { | public interface OperationHandle { | ||||
/** | /** | ||||
@@ -14,8 +16,9 @@ public interface OperationHandle { | |||||
boolean support(Class<?> operationType); | boolean support(Class<?> operationType); | ||||
/** | /** | ||||
* 解析和执行操作; | |||||
* | |||||
* 同步解析和执行操作; | |||||
* | |||||
* | |||||
* @param op | * @param op | ||||
* 操作实例; | * 操作实例; | ||||
* @param newBlockDataset | * @param newBlockDataset | ||||
@@ -24,8 +27,28 @@ public interface OperationHandle { | |||||
* 交易请求上下文; | * 交易请求上下文; | ||||
* @param previousBlockDataset | * @param previousBlockDataset | ||||
* 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | * 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | ||||
* | |||||
* @return 操作执行结果 | |||||
*/ | */ | ||||
void process(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, | |||||
BytesValue process(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, | |||||
LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService); | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService); | ||||
// /** | |||||
// * 异步解析和执行操作; | |||||
// * TODO 未来规划实现 | |||||
// * | |||||
// * | |||||
// * @param op | |||||
// * 操作实例; | |||||
// * @param newBlockDataset | |||||
// * 需要修改的新区块的数据集; | |||||
// * @param requestContext | |||||
// * 交易请求上下文; | |||||
// * @param previousBlockDataset | |||||
// * 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | |||||
// * | |||||
// * @return 操作执行结果 | |||||
// */ | |||||
// AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, | |||||
// LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService); | |||||
} | } |
@@ -3,7 +3,7 @@ package com.jd.blockchain.ledger.core; | |||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.crypto.PubKey; | import com.jd.blockchain.crypto.PubKey; | ||||
import com.jd.blockchain.ledger.BytesValue; | import com.jd.blockchain.ledger.BytesValue; | ||||
import com.jd.blockchain.ledger.BytesValueEntry; | |||||
import com.jd.blockchain.ledger.BytesData; | |||||
import com.jd.blockchain.ledger.UserInfo; | import com.jd.blockchain.ledger.UserInfo; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
@@ -50,12 +50,12 @@ public class UserAccount implements UserInfo { | |||||
public long setDataPubKey(PubKey pubKey) { | public long setDataPubKey(PubKey pubKey) { | ||||
byte[] pkBytes = pubKey.toBytes(); | byte[] pkBytes = pubKey.toBytes(); | ||||
return baseAccount.setBytes(DATA_PUB_KEY, BytesValueEntry.fromBytes(pkBytes), -1); | |||||
return baseAccount.setBytes(DATA_PUB_KEY, BytesData.fromBytes(pkBytes), -1); | |||||
} | } | ||||
public long setDataPubKey(PubKey pubKey, long version) { | public long setDataPubKey(PubKey pubKey, long version) { | ||||
byte[] pkBytes = pubKey.toBytes(); | byte[] pkBytes = pubKey.toBytes(); | ||||
return baseAccount.setBytes(DATA_PUB_KEY, BytesValueEntry.fromBytes(pkBytes), version); | |||||
return baseAccount.setBytes(DATA_PUB_KEY, BytesData.fromBytes(pkBytes), version); | |||||
} | } | ||||
public long setProperty(String key, String value, long version) { | public long setProperty(String key, String value, long version) { | ||||
@@ -63,7 +63,7 @@ public class UserAccount implements UserInfo { | |||||
} | } | ||||
public long setProperty(Bytes key, String value, long version) { | public long setProperty(Bytes key, String value, long version) { | ||||
return baseAccount.setBytes(encodePropertyKey(key), BytesValueEntry.fromText(value), version); | |||||
return baseAccount.setBytes(encodePropertyKey(key), BytesData.fromText(value), version); | |||||
} | } | ||||
public String getProperty(Bytes key) { | public String getProperty(Bytes key) { | ||||
@@ -3,16 +3,12 @@ package com.jd.blockchain.ledger.core.impl; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
import javax.annotation.PostConstruct; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||
import com.jd.blockchain.ledger.LedgerException; | import com.jd.blockchain.ledger.LedgerException; | ||||
import com.jd.blockchain.ledger.UserRegisterOperation; | |||||
import com.jd.blockchain.ledger.core.OperationHandle; | import com.jd.blockchain.ledger.core.OperationHandle; | ||||
import com.jd.blockchain.ledger.core.impl.handles.ContractCodeDeployOperationHandle; | import com.jd.blockchain.ledger.core.impl.handles.ContractCodeDeployOperationHandle; | ||||
import com.jd.blockchain.ledger.core.impl.handles.ContractEventSendOperationHandle; | |||||
import com.jd.blockchain.ledger.core.impl.handles.JVMContractEventSendOperationHandle; | |||||
import com.jd.blockchain.ledger.core.impl.handles.DataAccountKVSetOperationHandle; | import com.jd.blockchain.ledger.core.impl.handles.DataAccountKVSetOperationHandle; | ||||
import com.jd.blockchain.ledger.core.impl.handles.DataAccountRegisterOperationHandle; | import com.jd.blockchain.ledger.core.impl.handles.DataAccountRegisterOperationHandle; | ||||
import com.jd.blockchain.ledger.core.impl.handles.UserRegisterOperationHandle; | import com.jd.blockchain.ledger.core.impl.handles.UserRegisterOperationHandle; | ||||
@@ -21,18 +17,7 @@ import com.jd.blockchain.ledger.core.impl.handles.UserRegisterOperationHandle; | |||||
public class DefaultOperationHandleRegisteration implements OperationHandleRegisteration { | public class DefaultOperationHandleRegisteration implements OperationHandleRegisteration { | ||||
private List<OperationHandle> opHandles = new ArrayList<>(); | private List<OperationHandle> opHandles = new ArrayList<>(); | ||||
// private UserRegisterOperationHandle userRegHandle; | |||||
// | |||||
// private DataAccountRegisterOperationHandle dataAccRegHandle; | |||||
// | |||||
// private DataAccountKVSetOperationHandle dataAccKVSetHandle; | |||||
// | |||||
// private ContractCodeDeployOperationHandle contractDplHandle; | |||||
// | |||||
// private ContractEventSendOperationHandle contractEvtSendHandle; | |||||
public DefaultOperationHandleRegisteration() { | public DefaultOperationHandleRegisteration() { | ||||
initDefaultHandles(); | initDefaultHandles(); | ||||
} | } | ||||
@@ -40,25 +25,30 @@ public class DefaultOperationHandleRegisteration implements OperationHandleRegis | |||||
/** | /** | ||||
* 针对不采用bean依赖注入的方式来处理; | * 针对不采用bean依赖注入的方式来处理; | ||||
*/ | */ | ||||
private void initDefaultHandles(){ | |||||
private void initDefaultHandles() { | |||||
opHandles.add(new DataAccountKVSetOperationHandle()); | opHandles.add(new DataAccountKVSetOperationHandle()); | ||||
opHandles.add(new DataAccountRegisterOperationHandle()); | opHandles.add(new DataAccountRegisterOperationHandle()); | ||||
opHandles.add(new UserRegisterOperationHandle()); | opHandles.add(new UserRegisterOperationHandle()); | ||||
opHandles.add(new ContractCodeDeployOperationHandle()); | opHandles.add(new ContractCodeDeployOperationHandle()); | ||||
opHandles.add(new ContractEventSendOperationHandle()); | |||||
opHandles.add(new JVMContractEventSendOperationHandle()); | |||||
} | } | ||||
// @PostConstruct | |||||
// private void init() { | |||||
// opHandles.add(dataAccKVSetHandle); | |||||
// opHandles.add(dataAccRegHandle); | |||||
// opHandles.add(userRegHandle); | |||||
// opHandles.add(contractDplHandle); | |||||
// opHandles.add(contractEvtSendHandle); | |||||
// } | |||||
/* (non-Javadoc) | |||||
* @see com.jd.blockchain.ledger.core.impl.OperationHandleRegisteration#getHandle(java.lang.Class) | |||||
/** | |||||
* 以最高优先级插入一个操作处理器; | |||||
* | |||||
* @param handle | |||||
*/ | |||||
public void insertAsTopPriority(OperationHandle handle) { | |||||
opHandles.remove(handle); | |||||
opHandles.add(0, handle); | |||||
} | |||||
/* | |||||
* (non-Javadoc) | |||||
* | |||||
* @see | |||||
* com.jd.blockchain.ledger.core.impl.OperationHandleRegisteration#getHandle( | |||||
* java.lang.Class) | |||||
*/ | */ | ||||
@Override | @Override | ||||
public OperationHandle getHandle(Class<?> operationType) { | public OperationHandle getHandle(Class<?> operationType) { | ||||
@@ -69,6 +59,5 @@ public class DefaultOperationHandleRegisteration implements OperationHandleRegis | |||||
} | } | ||||
throw new LedgerException("Unsupported operation type[" + operationType.getName() + "]!"); | throw new LedgerException("Unsupported operation type[" + operationType.getName() + "]!"); | ||||
} | } | ||||
} | } |
@@ -1,85 +0,0 @@ | |||||
//package com.jd.blockchain.ledger.core.impl; | |||||
// | |||||
//import com.jd.blockchain.crypto.hash.HashDigest; | |||||
//import com.jd.blockchain.ledger.TransactionState; | |||||
//import com.jd.blockchain.ledger.LedgerBlock; | |||||
//import com.jd.blockchain.ledger.LedgerTransaction; | |||||
//import com.jd.blockchain.ledger.TransactionRequest; | |||||
//import com.jd.blockchain.ledger.core.LedgerDataSet; | |||||
//import com.jd.blockchain.ledger.core.LedgerEditor; | |||||
//import com.jd.blockchain.ledger.core.LedgerManage; | |||||
//import com.jd.blockchain.ledger.core.LedgerTransactionContext; | |||||
//import com.jd.blockchain.ledger.core.PrefixAppender; | |||||
//import com.jd.blockchain.storage.service.ExPolicyKVStorage; | |||||
//import com.jd.blockchain.storage.service.KVStorageService; | |||||
//import com.jd.blockchain.storage.service.VersioningKVStorage; | |||||
//import com.jd.blockchain.storage.service.utils.BufferedKVStorage; | |||||
// | |||||
///** | |||||
// * 账本初始化;<br> | |||||
// * | |||||
// * 初始生成账本时,所有的KV数据先缓冲写入到内存中,待计算得到账本 hash 之后,再重写入到与账本hash相关的持久化存储; | |||||
// * | |||||
// * @author huanghaiquan | |||||
// * | |||||
// */ | |||||
//class LedgerInitializer implements LedgerEditor { | |||||
// | |||||
// private KVStorageService baseStorage; | |||||
// | |||||
// private GenesisLedgerStorageProxy ledgerStorageProxy; | |||||
// | |||||
// private BufferedKVStorage genesisBufferedStorage; | |||||
// | |||||
// private LedgerEditor genesisBlockEditor; | |||||
// | |||||
// private LedgerBlock genesisBlock; | |||||
// | |||||
// private LedgerManage ledgerManager; | |||||
// | |||||
// LedgerInitializer(LedgerEditor genesisBlockEditor, BufferedKVStorage bufferedStorage, | |||||
// GenesisLedgerStorageProxy ledgerStorageProxy, KVStorageService kvStorage, LedgerManage ledgerManager) { | |||||
// this.genesisBlockEditor = genesisBlockEditor; | |||||
// this.genesisBufferedStorage = bufferedStorage; | |||||
// this.ledgerStorageProxy = ledgerStorageProxy; | |||||
// this.baseStorage = kvStorage; | |||||
// | |||||
// this.ledgerManager = ledgerManager; | |||||
// } | |||||
// | |||||
// @Override | |||||
// public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { | |||||
// return genesisBlockEditor.newTransaction(txRequest); | |||||
// } | |||||
// | |||||
// @Override | |||||
// public LedgerBlock prepare() { | |||||
// // create genesis block; | |||||
// genesisBlock = genesisBlockEditor.prepare(); | |||||
// | |||||
// return genesisBlock; | |||||
// } | |||||
// | |||||
// @Override | |||||
// public void commit() { | |||||
// // commit data of editor; it will flush data to genesisBufferedStorage; | |||||
// genesisBlockEditor.commit(); | |||||
// | |||||
// // redirect persistence to storage which created for this new ledger with ledger hash; | |||||
// HashDigest ledgerHash = genesisBlock.getHash(); | |||||
// String ledgerPrefix =LedgerManager.getLedgerStoragePrefix(ledgerHash); | |||||
// ExPolicyKVStorage ledgerExStorage = PrefixAppender.prefix(ledgerPrefix, baseStorage.getExPolicyKVStorage()); | |||||
// VersioningKVStorage ledgerVerStorage = PrefixAppender.prefix(ledgerPrefix, baseStorage.getVersioningKVStorage()); | |||||
// | |||||
// // ready to persistent; | |||||
// ledgerStorageProxy.setPersistentStorage(ledgerExStorage, ledgerVerStorage); | |||||
// | |||||
// // flush output; | |||||
// genesisBufferedStorage.flush(); | |||||
// } | |||||
// | |||||
// @Override | |||||
// public void cancel() { | |||||
// genesisBlockEditor.cancel(); | |||||
// } | |||||
//} |
@@ -5,19 +5,7 @@ import java.util.List; | |||||
import com.jd.blockchain.contract.ContractException; | import com.jd.blockchain.contract.ContractException; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.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.ledger.KVDataVO; | |||||
import com.jd.blockchain.ledger.KVInfoVO; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | |||||
import com.jd.blockchain.ledger.LedgerInfo; | |||||
import com.jd.blockchain.ledger.LedgerMetadata; | |||||
import com.jd.blockchain.ledger.LedgerTransaction; | |||||
import com.jd.blockchain.ledger.ParticipantNode; | |||||
import com.jd.blockchain.ledger.TransactionState; | |||||
import com.jd.blockchain.ledger.UserInfo; | |||||
import com.jd.blockchain.ledger.*; | |||||
import com.jd.blockchain.ledger.core.ContractAccountSet; | import com.jd.blockchain.ledger.core.ContractAccountSet; | ||||
import com.jd.blockchain.ledger.core.DataAccount; | import com.jd.blockchain.ledger.core.DataAccount; | ||||
import com.jd.blockchain.ledger.core.DataAccountSet; | import com.jd.blockchain.ledger.core.DataAccountSet; | ||||
@@ -276,15 +264,15 @@ public class LedgerQueryService implements BlockchainQueryService { | |||||
KVDataEntry[] entries = new KVDataEntry[keys.length]; | KVDataEntry[] entries = new KVDataEntry[keys.length]; | ||||
long ver; | long ver; | ||||
for (int i = 0; i < entries.length; i++) { | for (int i = 0; i < entries.length; i++) { | ||||
ver = dataAccount.getDataVersion(Bytes.fromString(keys[i])); | |||||
final String currKey = keys[i]; | |||||
dataAccount.getBytes(Bytes.fromString(keys[i]), 1); | |||||
ver = dataAccount.getDataVersion(Bytes.fromString(currKey)); | |||||
if (ver < 0) { | if (ver < 0) { | ||||
entries[i] = new KVDataObject(keys[i], -1, null); | |||||
entries[i] = new KVDataObject(currKey, -1, null); | |||||
} else { | } else { | ||||
BytesValue value = dataAccount.getBytes(Bytes.fromString(keys[i]), ver); | |||||
entries[i] = new KVDataObject(keys[i], ver, value); | |||||
BytesValue value = dataAccount.getBytes(Bytes.fromString(currKey), ver); | |||||
entries[i] = new KVDataObject(currKey, ver, value); | |||||
} | } | ||||
} | } | ||||
@@ -367,7 +355,7 @@ public class LedgerQueryService implements BlockchainQueryService { | |||||
} | } | ||||
@Override | @Override | ||||
public AccountHeader getContract(HashDigest ledgerHash, String address) { | |||||
public ContractInfo getContract(HashDigest ledgerHash, String address) { | |||||
LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | ||||
LedgerBlock block = ledger.getLatestBlock(); | LedgerBlock block = ledger.getLatestBlock(); | ||||
ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | ||||
@@ -1,8 +1,12 @@ | |||||
package com.jd.blockchain.ledger.core.impl; | package com.jd.blockchain.ledger.core.impl; | ||||
import java.util.Arrays; | |||||
import java.util.Comparator; | |||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.DigitalSignature; | import com.jd.blockchain.ledger.DigitalSignature; | ||||
import com.jd.blockchain.ledger.LedgerTransaction; | import com.jd.blockchain.ledger.LedgerTransaction; | ||||
import com.jd.blockchain.ledger.OperationResult; | |||||
import com.jd.blockchain.ledger.TransactionContent; | import com.jd.blockchain.ledger.TransactionContent; | ||||
import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
import com.jd.blockchain.ledger.TransactionState; | import com.jd.blockchain.ledger.TransactionState; | ||||
@@ -23,6 +27,8 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
private long blockHeight; | private long blockHeight; | ||||
private OperationResult[] operationResults; | |||||
// private HashDigest adminAccountHash; | // private HashDigest adminAccountHash; | ||||
// | // | ||||
// private HashDigest userAccountSetHash; | // private HashDigest userAccountSetHash; | ||||
@@ -49,7 +55,7 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
* 交易级的系统快照; | * 交易级的系统快照; | ||||
*/ | */ | ||||
public LedgerTransactionData(long blockHeight, TransactionRequest txReq, TransactionState execState, | public LedgerTransactionData(long blockHeight, TransactionRequest txReq, TransactionState execState, | ||||
TransactionStagedSnapshot txSnapshot) { | |||||
TransactionStagedSnapshot txSnapshot, OperationResult... opResults) { | |||||
this.blockHeight = blockHeight; | this.blockHeight = blockHeight; | ||||
// this.txSnapshot = txSnapshot == null ? new TransactionStagedSnapshot() : txSnapshot; | // this.txSnapshot = txSnapshot == null ? new TransactionStagedSnapshot() : txSnapshot; | ||||
this.txSnapshot = txSnapshot; | this.txSnapshot = txSnapshot; | ||||
@@ -57,6 +63,15 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
this.endpointSignatures = txReq.getEndpointSignatures(); | this.endpointSignatures = txReq.getEndpointSignatures(); | ||||
this.nodeSignatures = txReq.getNodeSignatures(); | this.nodeSignatures = txReq.getNodeSignatures(); | ||||
this.executionState = execState; | this.executionState = execState; | ||||
if (opResults != null) { | |||||
Arrays.sort(opResults, new Comparator<OperationResult>() { | |||||
@Override | |||||
public int compare(OperationResult o1, OperationResult o2) { | |||||
return o1.getIndex() - o2.getIndex(); | |||||
} | |||||
}); | |||||
} | |||||
this.operationResults = opResults; | |||||
} | } | ||||
@Override | @Override | ||||
@@ -74,6 +89,11 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
return executionState; | return executionState; | ||||
} | } | ||||
@Override | |||||
public OperationResult[] getOperationResults() { | |||||
return operationResults; | |||||
} | |||||
@Override | @Override | ||||
public TransactionContent getTransactionContent() { | public TransactionContent getTransactionContent() { | ||||
return this.transactionContent; | return this.transactionContent; | ||||
@@ -1,5 +1,6 @@ | |||||
package com.jd.blockchain.ledger.core.impl; | package com.jd.blockchain.ledger.core.impl; | ||||
import java.util.List; | |||||
import java.util.Stack; | import java.util.Stack; | ||||
import com.jd.blockchain.binaryproto.BinaryProtocol; | import com.jd.blockchain.binaryproto.BinaryProtocol; | ||||
@@ -351,6 +352,11 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
@Override | @Override | ||||
public LedgerTransaction commit(TransactionState txResult) { | public LedgerTransaction commit(TransactionState txResult) { | ||||
return commit(txResult, null); | |||||
} | |||||
@Override | |||||
public LedgerTransaction commit(TransactionState txResult, List<OperationResult> operationResults) { | |||||
checkTxState(); | checkTxState(); | ||||
// capture snapshot | // capture snapshot | ||||
@@ -359,7 +365,8 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
// LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | ||||
// txResult, txDataSnapshot); | // txResult, txDataSnapshot); | ||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null); | |||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null, operationResultArray(operationResults)); | |||||
this.txset.add(tx); | this.txset.add(tx); | ||||
// this.txset.commit(); | // this.txset.commit(); | ||||
@@ -377,6 +384,11 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
@Override | @Override | ||||
public LedgerTransaction discardAndCommit(TransactionState txResult) { | public LedgerTransaction discardAndCommit(TransactionState txResult) { | ||||
return discardAndCommit(txResult, null); | |||||
} | |||||
@Override | |||||
public LedgerTransaction discardAndCommit(TransactionState txResult, List<OperationResult> operationResults) { | |||||
checkTxState(); | checkTxState(); | ||||
// 未处理 | // 未处理 | ||||
@@ -385,7 +397,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
// TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); | // TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); | ||||
// LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | ||||
// txResult, txDataSnapshot); | // txResult, txDataSnapshot); | ||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null); | |||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null, operationResultArray(operationResults)); | |||||
this.txset.add(tx); | this.txset.add(tx); | ||||
// this.txset.commit(); | // this.txset.commit(); | ||||
@@ -410,6 +422,15 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
return txDataSnapshot; | return txDataSnapshot; | ||||
} | } | ||||
private OperationResult[] operationResultArray(List<OperationResult> operationResults) { | |||||
OperationResult[] operationResultArray = null; | |||||
if (operationResults != null && !operationResults.isEmpty()) { | |||||
operationResultArray = new OperationResult[operationResults.size()]; | |||||
operationResults.toArray(operationResultArray); | |||||
} | |||||
return operationResultArray; | |||||
} | |||||
@Override | @Override | ||||
public void rollback() { | public void rollback() { | ||||
if (this.rollbacked) { | if (this.rollbacked) { | ||||
@@ -3,16 +3,17 @@ package com.jd.blockchain.ledger.core.impl; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.concurrent.CompletableFuture; | |||||
import com.jd.blockchain.ledger.core.impl.handles.ContractEventSendOperationHandle; | |||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | import com.jd.blockchain.ledger.LedgerBlock; | ||||
import com.jd.blockchain.ledger.LedgerException; | import com.jd.blockchain.ledger.LedgerException; | ||||
import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
import com.jd.blockchain.ledger.OperationResult; | |||||
import com.jd.blockchain.ledger.OperationResultData; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
import com.jd.blockchain.ledger.TransactionResponse; | import com.jd.blockchain.ledger.TransactionResponse; | ||||
import com.jd.blockchain.ledger.TransactionState; | import com.jd.blockchain.ledger.TransactionState; | ||||
@@ -30,7 +31,7 @@ import com.jd.blockchain.utils.Bytes; | |||||
public class TransactionBatchProcessor implements TransactionBatchProcess { | public class TransactionBatchProcessor implements TransactionBatchProcess { | ||||
private static final Logger LOGGER = LoggerFactory.getLogger(TransactionBatchProcessor.class); | private static final Logger LOGGER = LoggerFactory.getLogger(TransactionBatchProcessor.class); | ||||
private LedgerService ledgerService; | private LedgerService ledgerService; | ||||
private LedgerEditor newBlockEditor; | private LedgerEditor newBlockEditor; | ||||
@@ -76,8 +77,11 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
// 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | ||||
LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | ||||
TransactionState result; | TransactionState result; | ||||
List<CompletableFuture<String>> contractReturn = new ArrayList<>(); | |||||
List<OperationResult> operationResults = new ArrayList<>(); | |||||
try { | try { | ||||
LedgerDataSet dataset = txCtx.getDataSet(); | LedgerDataSet dataset = txCtx.getDataSet(); | ||||
TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | ||||
// TODO: 验证签名者的有效性; | // TODO: 验证签名者的有效性; | ||||
@@ -103,46 +107,37 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
} | } | ||||
}; | }; | ||||
OperationHandle opHandle; | OperationHandle opHandle; | ||||
int opIndex = 0; | |||||
for (Operation op : ops) { | for (Operation op : ops) { | ||||
opHandle = opHandles.getHandle(op.getClass()); | opHandle = opHandles.getHandle(op.getClass()); | ||||
// 合约执行需要填充执行结果 | |||||
if (opHandle instanceof ContractEventSendOperationHandle) { | |||||
CompletableFuture<String> currContractReturn = new CompletableFuture<>(); | |||||
contractReturn.add(currContractReturn); | |||||
((ContractEventSendOperationHandle) opHandle).process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService, currContractReturn); | |||||
} else { | |||||
opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | |||||
BytesValue opResult = opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | |||||
if (opResult != null) { | |||||
operationResults.add(new OperationResultData(opIndex, opResult)); | |||||
} | } | ||||
opIndex++; | |||||
} | } | ||||
// 提交交易(事务); | // 提交交易(事务); | ||||
result = TransactionState.SUCCESS; | result = TransactionState.SUCCESS; | ||||
txCtx.commit(result); | |||||
txCtx.commit(result, operationResults); | |||||
} catch (LedgerException e) { | } catch (LedgerException e) { | ||||
// TODO: 识别更详细的异常类型以及执行对应的处理; | // TODO: 识别更详细的异常类型以及执行对应的处理; | ||||
result = TransactionState.LEDGER_ERROR; | result = TransactionState.LEDGER_ERROR; | ||||
txCtx.discardAndCommit(TransactionState.LEDGER_ERROR); | |||||
txCtx.discardAndCommit(TransactionState.LEDGER_ERROR, operationResults); | |||||
LOGGER.warn(String.format("Transaction rollback caused by the ledger exception! --[TxHash=%s] --%s", | LOGGER.warn(String.format("Transaction rollback caused by the ledger exception! --[TxHash=%s] --%s", | ||||
request.getHash().toBase58(), e.getMessage()), e); | request.getHash().toBase58(), e.getMessage()), e); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
result = TransactionState.SYSTEM_ERROR; | result = TransactionState.SYSTEM_ERROR; | ||||
txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR); | |||||
txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR, operationResults); | |||||
LOGGER.warn(String.format("Transaction rollback caused by the system exception! --[TxHash=%s] --%s", | LOGGER.warn(String.format("Transaction rollback caused by the system exception! --[TxHash=%s] --%s", | ||||
request.getHash().toBase58(), e.getMessage()), e); | request.getHash().toBase58(), e.getMessage()), e); | ||||
} | } | ||||
TxResponseHandle resp = new TxResponseHandle(request, result); | TxResponseHandle resp = new TxResponseHandle(request, result); | ||||
if (!contractReturn.isEmpty()) { | |||||
// 获取结果中的字符串 | |||||
String[] returnValue = new String[contractReturn.size()]; | |||||
try { | |||||
for (int i = 0; i < contractReturn.size(); i++) { | |||||
returnValue[i] = contractReturn.get(i).get(); | |||||
} | |||||
resp.setContractReturn(returnValue); | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
if (!operationResults.isEmpty()) { | |||||
OperationResult[] operationResultArray = new OperationResult[operationResults.size()]; | |||||
resp.setOperationResults(operationResults.toArray(operationResultArray)); | |||||
} | } | ||||
responseList.add(resp); | responseList.add(resp); | ||||
@@ -224,7 +219,7 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
private TransactionState result; | private TransactionState result; | ||||
private String[] contractReturn; | |||||
private OperationResult[] operationResults; | |||||
public TxResponseHandle(TransactionRequest request, TransactionState result) { | public TxResponseHandle(TransactionRequest request, TransactionState result) { | ||||
this.request = request; | this.request = request; | ||||
@@ -257,12 +252,12 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
} | } | ||||
@Override | @Override | ||||
public String[] getContractReturn() { | |||||
return contractReturn; | |||||
public OperationResult[] getOperationResults() { | |||||
return operationResults; | |||||
} | } | ||||
public void setContractReturn(String[] contractReturn) { | |||||
this.contractReturn = contractReturn; | |||||
public void setOperationResults(OperationResult[] operationResults) { | |||||
this.operationResults = operationResults; | |||||
} | } | ||||
} | } | ||||
@@ -1,13 +1,10 @@ | |||||
package com.jd.blockchain.ledger.core.impl.handles; | package com.jd.blockchain.ledger.core.impl.handles; | ||||
import static com.jd.blockchain.utils.BaseConstant.CONTRACT_SERVICE_PROVIDER; | |||||
import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
import com.jd.blockchain.contract.LocalContractEventContext; | import com.jd.blockchain.contract.LocalContractEventContext; | ||||
import com.jd.blockchain.contract.engine.ContractCode; | import com.jd.blockchain.contract.engine.ContractCode; | ||||
import com.jd.blockchain.contract.engine.ContractEngine; | |||||
import com.jd.blockchain.contract.engine.ContractServiceProviders; | |||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.ledger.ContractEventSendOperation; | import com.jd.blockchain.ledger.ContractEventSendOperation; | ||||
import com.jd.blockchain.ledger.LedgerException; | import com.jd.blockchain.ledger.LedgerException; | ||||
import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
@@ -20,33 +17,17 @@ import com.jd.blockchain.ledger.core.TransactionRequestContext; | |||||
import com.jd.blockchain.ledger.core.impl.LedgerQueryService; | import com.jd.blockchain.ledger.core.impl.LedgerQueryService; | ||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
import java.util.concurrent.CompletableFuture; | |||||
import java.util.concurrent.Future; | |||||
@Service | @Service | ||||
public class ContractEventSendOperationHandle implements OperationHandle { | |||||
private static final ContractEngine JVM_ENGINE; | |||||
static { | |||||
JVM_ENGINE = ContractServiceProviders.getProvider(CONTRACT_SERVICE_PROVIDER).getEngine(); | |||||
} | |||||
@Override | |||||
public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { | |||||
process(op, dataset, requestContext, previousBlockDataset, opHandleContext, ledgerService, null); | |||||
} | |||||
public abstract class AbtractContractEventHandle implements OperationHandle { | |||||
@Override | @Override | ||||
public boolean support(Class<?> operationType) { | public boolean support(Class<?> operationType) { | ||||
return ContractEventSendOperation.class.isAssignableFrom(operationType); | return ContractEventSendOperation.class.isAssignableFrom(operationType); | ||||
} | } | ||||
public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, | |||||
LedgerService ledgerService, CompletableFuture<String> contractReturn) { | |||||
@Override | |||||
public BytesValue process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { | |||||
ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | ||||
// 先从账本校验合约的有效性; | // 先从账本校验合约的有效性; | ||||
// 注意:必须在前一个区块的数据集中进行校验,因为那是经过共识的数据;从当前新区块链数据集校验则会带来攻击风险:未经共识的合约得到执行; | // 注意:必须在前一个区块的数据集中进行校验,因为那是经过共识的数据;从当前新区块链数据集校验则会带来攻击风险:未经共识的合约得到执行; | ||||
@@ -73,16 +54,15 @@ public class ContractEventSendOperationHandle implements OperationHandle { | |||||
localContractEventContext.setArgs(contractOP.getArgs()).setTransactionRequest(requestContext.getRequest()) | localContractEventContext.setArgs(contractOP.getArgs()).setTransactionRequest(requestContext.getRequest()) | ||||
.setLedgerContext(ledgerContext); | .setLedgerContext(ledgerContext); | ||||
ContractCode contractCode = JVM_ENGINE.getContract(contract.getAddress(), contract.getChaincodeVersion()); | |||||
if (contractCode == null) { | |||||
// 装载合约; | |||||
contractCode = JVM_ENGINE.setupContract(contract.getAddress(), contract.getChaincodeVersion(), | |||||
contract.getChainCode()); | |||||
} | |||||
// 装载合约; | |||||
ContractCode contractCode = loadContractCode(contract); | |||||
// 处理合约事件; | // 处理合约事件; | ||||
contractCode.processEvent(localContractEventContext, contractReturn); | |||||
return contractCode.processEvent(localContractEventContext); | |||||
} | } | ||||
protected abstract ContractCode loadContractCode(ContractAccount contract); | |||||
} | } |
@@ -1,5 +1,8 @@ | |||||
package com.jd.blockchain.ledger.core.impl.handles; | package com.jd.blockchain.ledger.core.impl.handles; | ||||
import org.springframework.stereotype.Service; | |||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.ledger.ContractCodeDeployOperation; | import com.jd.blockchain.ledger.ContractCodeDeployOperation; | ||||
import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
import com.jd.blockchain.ledger.core.LedgerDataSet; | import com.jd.blockchain.ledger.core.LedgerDataSet; | ||||
@@ -8,22 +11,28 @@ import com.jd.blockchain.ledger.core.OperationHandle; | |||||
import com.jd.blockchain.ledger.core.TransactionRequestContext; | import com.jd.blockchain.ledger.core.TransactionRequestContext; | ||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
import org.springframework.stereotype.Service; | |||||
@Service | @Service | ||||
public class ContractCodeDeployOperationHandle implements OperationHandle { | public class ContractCodeDeployOperationHandle implements OperationHandle { | ||||
@Override | @Override | ||||
public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
public BytesValue process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | ||||
ContractCodeDeployOperation contractOP = (ContractCodeDeployOperation) op; | ContractCodeDeployOperation contractOP = (ContractCodeDeployOperation) op; | ||||
// TODO: 校验合约代码的正确性; | // TODO: 校验合约代码的正确性; | ||||
// TODO: 请求者应该提供合约账户的公钥签名,已确定注册的地址的唯一性; | // TODO: 请求者应该提供合约账户的公钥签名,已确定注册的地址的唯一性; | ||||
dataset.getContractAccountSet().deploy(contractOP.getContractID().getAddress(), | dataset.getContractAccountSet().deploy(contractOP.getContractID().getAddress(), | ||||
contractOP.getContractID().getPubKey(), contractOP.getAddressSignature(), contractOP.getChainCode()); | contractOP.getContractID().getPubKey(), contractOP.getAddressSignature(), contractOP.getChainCode()); | ||||
return null; | |||||
} | } | ||||
// @Override | |||||
// public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||||
// return null; | |||||
// } | |||||
@Override | @Override | ||||
public boolean support(Class<?> operationType) { | public boolean support(Class<?> operationType) { | ||||
return ContractCodeDeployOperation.class.isAssignableFrom(operationType); | return ContractCodeDeployOperation.class.isAssignableFrom(operationType); | ||||
@@ -5,23 +5,7 @@ import java.util.List; | |||||
import com.jd.blockchain.contract.LedgerContext; | import com.jd.blockchain.contract.LedgerContext; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.AccountHeader; | |||||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.ledger.BytesValueEntry; | |||||
import com.jd.blockchain.ledger.DataAccountKVSetOperation; | |||||
import com.jd.blockchain.ledger.DataAccountRegisterOperation; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | |||||
import com.jd.blockchain.ledger.KVInfoVO; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | |||||
import com.jd.blockchain.ledger.LedgerInfo; | |||||
import com.jd.blockchain.ledger.LedgerMetadata; | |||||
import com.jd.blockchain.ledger.LedgerTransaction; | |||||
import com.jd.blockchain.ledger.Operation; | |||||
import com.jd.blockchain.ledger.ParticipantNode; | |||||
import com.jd.blockchain.ledger.TransactionState; | |||||
import com.jd.blockchain.ledger.UserInfo; | |||||
import com.jd.blockchain.ledger.UserRegisterOperation; | |||||
import com.jd.blockchain.ledger.*; | |||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
import com.jd.blockchain.transaction.BlockchainQueryService; | import com.jd.blockchain.transaction.BlockchainQueryService; | ||||
import com.jd.blockchain.transaction.DataAccountKVSetOperationBuilder; | import com.jd.blockchain.transaction.DataAccountKVSetOperationBuilder; | ||||
@@ -186,7 +170,7 @@ public class ContractLedgerContext implements LedgerContext { | |||||
} | } | ||||
@Override | @Override | ||||
public AccountHeader getContract(HashDigest ledgerHash, String address) { | |||||
public ContractInfo getContract(HashDigest ledgerHash, String address) { | |||||
return innerQueryService.getContract(ledgerHash, address); | return innerQueryService.getContract(ledgerHash, address); | ||||
} | } | ||||
@@ -279,7 +263,7 @@ public class ContractLedgerContext implements LedgerContext { | |||||
@Override | @Override | ||||
public DataAccountKVSetOperationBuilder setText(String key, String value, long expVersion) { | public DataAccountKVSetOperationBuilder setText(String key, String value, long expVersion) { | ||||
BytesValue bytesValue = BytesValueEntry.fromText(value); | |||||
BytesValue bytesValue = BytesData.fromText(value); | |||||
this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | ||||
handle(op); | handle(op); | ||||
return this; | return this; | ||||
@@ -287,7 +271,7 @@ public class ContractLedgerContext implements LedgerContext { | |||||
@Override | @Override | ||||
public DataAccountKVSetOperationBuilder setBytes(String key, Bytes value, long expVersion) { | public DataAccountKVSetOperationBuilder setBytes(String key, Bytes value, long expVersion) { | ||||
BytesValue bytesValue = BytesValueEntry.fromBytes(value); | |||||
BytesValue bytesValue = BytesData.fromBytes(value); | |||||
this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | ||||
handle(op); | handle(op); | ||||
return this; | return this; | ||||
@@ -295,7 +279,7 @@ public class ContractLedgerContext implements LedgerContext { | |||||
@Override | @Override | ||||
public DataAccountKVSetOperationBuilder setInt64(String key, long value, long expVersion) { | public DataAccountKVSetOperationBuilder setInt64(String key, long value, long expVersion) { | ||||
BytesValue bytesValue = BytesValueEntry.fromInt64(value); | |||||
BytesValue bytesValue = BytesData.fromInt64(value); | |||||
this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | ||||
handle(op); | handle(op); | ||||
return this; | return this; | ||||
@@ -312,7 +296,7 @@ public class ContractLedgerContext implements LedgerContext { | |||||
@Override | @Override | ||||
public DataAccountKVSetOperationBuilder setJSON(String key, String value, long expVersion) { | public DataAccountKVSetOperationBuilder setJSON(String key, String value, long expVersion) { | ||||
BytesValue bytesValue = BytesValueEntry.fromJSON(value); | |||||
BytesValue bytesValue = BytesData.fromJSON(value); | |||||
this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | ||||
handle(op); | handle(op); | ||||
return this; | return this; | ||||
@@ -320,7 +304,7 @@ public class ContractLedgerContext implements LedgerContext { | |||||
@Override | @Override | ||||
public DataAccountKVSetOperationBuilder setXML(String key, String value, long expVersion) { | public DataAccountKVSetOperationBuilder setXML(String key, String value, long expVersion) { | ||||
BytesValue bytesValue = BytesValueEntry.fromXML(value); | |||||
BytesValue bytesValue = BytesData.fromXML(value); | |||||
this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | ||||
handle(op); | handle(op); | ||||
return this; | return this; | ||||
@@ -328,7 +312,7 @@ public class ContractLedgerContext implements LedgerContext { | |||||
@Override | @Override | ||||
public DataAccountKVSetOperationBuilder setBytes(String key, byte[] value, long expVersion) { | public DataAccountKVSetOperationBuilder setBytes(String key, byte[] value, long expVersion) { | ||||
BytesValue bytesValue = BytesValueEntry.fromBytes(value); | |||||
BytesValue bytesValue = BytesData.fromBytes(value); | |||||
this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | ||||
handle(op); | handle(op); | ||||
return this; | return this; | ||||
@@ -336,7 +320,7 @@ public class ContractLedgerContext implements LedgerContext { | |||||
@Override | @Override | ||||
public DataAccountKVSetOperationBuilder setImage(String key, byte[] value, long expVersion) { | public DataAccountKVSetOperationBuilder setImage(String key, byte[] value, long expVersion) { | ||||
BytesValue bytesValue = BytesValueEntry.fromImage(value); | |||||
BytesValue bytesValue = BytesData.fromImage(value); | |||||
this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | ||||
handle(op); | handle(op); | ||||
return this; | return this; | ||||
@@ -344,7 +328,7 @@ public class ContractLedgerContext implements LedgerContext { | |||||
@Override | @Override | ||||
public DataAccountKVSetOperationBuilder setTimestamp(String key, long value, long expVersion) { | public DataAccountKVSetOperationBuilder setTimestamp(String key, long value, long expVersion) { | ||||
BytesValue bytesValue = BytesValueEntry.fromTimestamp(value); | |||||
BytesValue bytesValue = BytesData.fromTimestamp(value); | |||||
this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | ||||
handle(op); | handle(op); | ||||
return this; | return this; | ||||
@@ -1,33 +1,44 @@ | |||||
package com.jd.blockchain.ledger.core.impl.handles; | package com.jd.blockchain.ledger.core.impl.handles; | ||||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
import org.springframework.stereotype.Service; | |||||
import com.jd.blockchain.binaryproto.DataContractRegistry; | import com.jd.blockchain.binaryproto.DataContractRegistry; | ||||
import com.jd.blockchain.ledger.BytesValue; | import com.jd.blockchain.ledger.BytesValue; | ||||
import com.jd.blockchain.ledger.DataAccountKVSetOperation; | import com.jd.blockchain.ledger.DataAccountKVSetOperation; | ||||
import com.jd.blockchain.ledger.DataAccountKVSetOperation.KVWriteEntry; | import com.jd.blockchain.ledger.DataAccountKVSetOperation.KVWriteEntry; | ||||
import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
import com.jd.blockchain.ledger.core.*; | |||||
import com.jd.blockchain.ledger.core.DataAccount; | |||||
import com.jd.blockchain.ledger.core.LedgerDataSet; | |||||
import com.jd.blockchain.ledger.core.LedgerService; | |||||
import com.jd.blockchain.ledger.core.OperationHandle; | |||||
import com.jd.blockchain.ledger.core.TransactionRequestContext; | |||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
import org.springframework.stereotype.Service; | |||||
@Service | @Service | ||||
public class DataAccountKVSetOperationHandle implements OperationHandle{ | |||||
public class DataAccountKVSetOperationHandle implements OperationHandle { | |||||
static { | static { | ||||
DataContractRegistry.register(BytesValue.class); | DataContractRegistry.register(BytesValue.class); | ||||
} | } | ||||
@Override | @Override | ||||
public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
public BytesValue process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | ||||
DataAccountKVSetOperation kvWriteOp = (DataAccountKVSetOperation) op; | DataAccountKVSetOperation kvWriteOp = (DataAccountKVSetOperation) op; | ||||
DataAccount account = dataset.getDataAccountSet().getDataAccount(kvWriteOp.getAccountAddress()); | DataAccount account = dataset.getDataAccountSet().getDataAccount(kvWriteOp.getAccountAddress()); | ||||
KVWriteEntry[] writeset = kvWriteOp.getWriteSet(); | |||||
for (KVWriteEntry kvw : writeset) { | |||||
KVWriteEntry[] writeSet = kvWriteOp.getWriteSet(); | |||||
for (KVWriteEntry kvw : writeSet) { | |||||
// byte[] value = BinaryProtocol.encode(kvw.getValue(), BytesValue.class); | |||||
account.setBytes(Bytes.fromString(kvw.getKey()), kvw.getValue(), kvw.getExpectedVersion()); | account.setBytes(Bytes.fromString(kvw.getKey()), kvw.getValue(), kvw.getExpectedVersion()); | ||||
} | } | ||||
return null; | |||||
} | } | ||||
// @Override | |||||
// public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||||
// return null; | |||||
// } | |||||
@Override | @Override | ||||
public boolean support(Class<?> operationType) { | public boolean support(Class<?> operationType) { | ||||
return DataAccountKVSetOperation.class.isAssignableFrom(operationType); | return DataAccountKVSetOperation.class.isAssignableFrom(operationType); | ||||
@@ -1,6 +1,9 @@ | |||||
package com.jd.blockchain.ledger.core.impl.handles; | package com.jd.blockchain.ledger.core.impl.handles; | ||||
import org.springframework.stereotype.Service; | |||||
import com.jd.blockchain.ledger.BlockchainIdentity; | import com.jd.blockchain.ledger.BlockchainIdentity; | ||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.ledger.DataAccountRegisterOperation; | import com.jd.blockchain.ledger.DataAccountRegisterOperation; | ||||
import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
import com.jd.blockchain.ledger.core.LedgerDataSet; | import com.jd.blockchain.ledger.core.LedgerDataSet; | ||||
@@ -9,23 +12,28 @@ import com.jd.blockchain.ledger.core.OperationHandle; | |||||
import com.jd.blockchain.ledger.core.TransactionRequestContext; | import com.jd.blockchain.ledger.core.TransactionRequestContext; | ||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
import org.springframework.stereotype.Service; | |||||
@Service | @Service | ||||
public class DataAccountRegisterOperationHandle implements OperationHandle{ | |||||
public class DataAccountRegisterOperationHandle implements OperationHandle { | |||||
@Override | @Override | ||||
public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
public BytesValue process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | ||||
DataAccountRegisterOperation dataAccountRegOp = (DataAccountRegisterOperation) op; | DataAccountRegisterOperation dataAccountRegOp = (DataAccountRegisterOperation) op; | ||||
BlockchainIdentity bid = dataAccountRegOp.getAccountID(); | BlockchainIdentity bid = dataAccountRegOp.getAccountID(); | ||||
//TODO: 校验用户身份; | //TODO: 校验用户身份; | ||||
//TODO: 请求者应该提供数据账户的公钥签名,已确定注册的地址的唯一性; | //TODO: 请求者应该提供数据账户的公钥签名,已确定注册的地址的唯一性; | ||||
dataset.getDataAccountSet().register(bid.getAddress(), bid.getPubKey(), null); | dataset.getDataAccountSet().register(bid.getAddress(), bid.getPubKey(), null); | ||||
return null; | |||||
} | } | ||||
// @Override | |||||
// public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||||
// return null; | |||||
// } | |||||
@Override | @Override | ||||
public boolean support(Class<?> operationType) { | public boolean support(Class<?> operationType) { | ||||
return DataAccountRegisterOperation.class.isAssignableFrom(operationType); | return DataAccountRegisterOperation.class.isAssignableFrom(operationType); | ||||
@@ -0,0 +1,43 @@ | |||||
package com.jd.blockchain.ledger.core.impl.handles; | |||||
import static com.jd.blockchain.utils.BaseConstant.CONTRACT_SERVICE_PROVIDER; | |||||
import com.jd.blockchain.contract.engine.ContractCode; | |||||
import com.jd.blockchain.contract.engine.ContractEngine; | |||||
import com.jd.blockchain.contract.engine.ContractServiceProviders; | |||||
import com.jd.blockchain.ledger.Operation; | |||||
import com.jd.blockchain.ledger.core.ContractAccount; | |||||
import com.jd.blockchain.ledger.core.LedgerDataSet; | |||||
import com.jd.blockchain.ledger.core.LedgerService; | |||||
import com.jd.blockchain.ledger.core.TransactionRequestContext; | |||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | |||||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
public class JVMContractEventSendOperationHandle extends AbtractContractEventHandle { | |||||
private static final ContractEngine JVM_ENGINE; | |||||
static { | |||||
JVM_ENGINE = ContractServiceProviders.getProvider(CONTRACT_SERVICE_PROVIDER).getEngine(); | |||||
} | |||||
@Override | |||||
protected ContractCode loadContractCode(ContractAccount contract) { | |||||
ContractCode contractCode = JVM_ENGINE.getContract(contract.getAddress(), contract.getChaincodeVersion()); | |||||
if (contractCode == null) { | |||||
// 装载合约; | |||||
contractCode = JVM_ENGINE.setupContract(contract.getAddress(), contract.getChaincodeVersion(), | |||||
contract.getChainCode()); | |||||
} | |||||
return contractCode; | |||||
} | |||||
// @Override | |||||
// public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, | |||||
// TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, | |||||
// OperationHandleContext handleContext, LedgerService ledgerService) { | |||||
// // TODO Auto-generated method stub | |||||
// return null; | |||||
// } | |||||
} |
@@ -1,6 +1,7 @@ | |||||
package com.jd.blockchain.ledger.core.impl.handles; | package com.jd.blockchain.ledger.core.impl.handles; | ||||
import com.jd.blockchain.ledger.BlockchainIdentity; | import com.jd.blockchain.ledger.BlockchainIdentity; | ||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
import com.jd.blockchain.ledger.UserRegisterOperation; | import com.jd.blockchain.ledger.UserRegisterOperation; | ||||
import com.jd.blockchain.ledger.core.LedgerDataSet; | import com.jd.blockchain.ledger.core.LedgerDataSet; | ||||
@@ -8,15 +9,24 @@ import com.jd.blockchain.ledger.core.LedgerService; | |||||
import com.jd.blockchain.ledger.core.OperationHandle; | import com.jd.blockchain.ledger.core.OperationHandle; | ||||
import com.jd.blockchain.ledger.core.TransactionRequestContext; | import com.jd.blockchain.ledger.core.TransactionRequestContext; | ||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
import com.jd.blockchain.utils.Bytes; | |||||
public class UserRegisterOperationHandle implements OperationHandle{ | |||||
public class UserRegisterOperationHandle implements OperationHandle { | |||||
@Override | @Override | ||||
public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
public BytesValue process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | ||||
UserRegisterOperation userRegOp = (UserRegisterOperation) op; | UserRegisterOperation userRegOp = (UserRegisterOperation) op; | ||||
BlockchainIdentity bid = userRegOp.getUserID(); | BlockchainIdentity bid = userRegOp.getUserID(); | ||||
dataset.getUserAccountSet().register(bid.getAddress(), bid.getPubKey()); | |||||
Bytes userAddress = bid.getAddress(); | |||||
dataset.getUserAccountSet().register(userAddress, bid.getPubKey()); | |||||
return null; | |||||
} | } | ||||
@Override | @Override | ||||
@@ -12,7 +12,7 @@ import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | |||||
import com.jd.blockchain.crypto.service.sm.SMCryptoService; | import com.jd.blockchain.crypto.service.sm.SMCryptoService; | ||||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | import com.jd.blockchain.ledger.BlockchainKeyGenerator; | ||||
import com.jd.blockchain.ledger.BlockchainKeypair; | import com.jd.blockchain.ledger.BlockchainKeypair; | ||||
import com.jd.blockchain.ledger.BytesValueEntry; | |||||
import com.jd.blockchain.ledger.BytesData; | |||||
import com.jd.blockchain.ledger.core.BaseAccount; | import com.jd.blockchain.ledger.core.BaseAccount; | ||||
import com.jd.blockchain.ledger.core.CryptoConfig; | import com.jd.blockchain.ledger.core.CryptoConfig; | ||||
import com.jd.blockchain.storage.service.utils.MemoryKVStorage; | import com.jd.blockchain.storage.service.utils.MemoryKVStorage; | ||||
@@ -53,33 +53,33 @@ public class BaseAccountTest { | |||||
assertFalse(baseAccount.isReadonly()); | assertFalse(baseAccount.isReadonly()); | ||||
// 在空白状态下写入数据; | // 在空白状态下写入数据; | ||||
long v = baseAccount.setBytes(Bytes.fromString("A"), BytesValueEntry.fromText("VALUE_A"), 0); | |||||
long v = baseAccount.setBytes(Bytes.fromString("A"), BytesData.fromText("VALUE_A"), 0); | |||||
// 预期失败; | // 预期失败; | ||||
assertEquals(-1, v); | assertEquals(-1, v); | ||||
v = baseAccount.setBytes(Bytes.fromString("A"), BytesValueEntry.fromText("VALUE_A"), 1); | |||||
v = baseAccount.setBytes(Bytes.fromString("A"), BytesData.fromText("VALUE_A"), 1); | |||||
// 预期失败; | // 预期失败; | ||||
assertEquals(-1, v); | assertEquals(-1, v); | ||||
v = baseAccount.setBytes(Bytes.fromString("A"), BytesValueEntry.fromText("VALUE_A"), -1); | |||||
v = baseAccount.setBytes(Bytes.fromString("A"), BytesData.fromText("VALUE_A"), -1); | |||||
// 预期成功; | // 预期成功; | ||||
assertEquals(0, v); | assertEquals(0, v); | ||||
v = baseAccount.setBytes(Bytes.fromString("A"), BytesValueEntry.fromText("VALUE_A-1"), -1); | |||||
v = baseAccount.setBytes(Bytes.fromString("A"), BytesData.fromText("VALUE_A-1"), -1); | |||||
// 已经存在版本,指定版本号-1,预期导致失败; | // 已经存在版本,指定版本号-1,预期导致失败; | ||||
assertEquals(-1, v); | assertEquals(-1, v); | ||||
baseAccount.commit(); | baseAccount.commit(); | ||||
v = 0; | v = 0; | ||||
for (int i = 0; i < 10; i++) { | for (int i = 0; i < 10; i++) { | ||||
long s = baseAccount.setBytes(Bytes.fromString("A"), BytesValueEntry.fromText("VALUE_A_" + i), v); | |||||
long s = baseAccount.setBytes(Bytes.fromString("A"), BytesData.fromText("VALUE_A_" + i), v); | |||||
baseAccount.commit(); | baseAccount.commit(); | ||||
// 预期成功; | // 预期成功; | ||||
assertEquals(v + 1, s); | assertEquals(v + 1, s); | ||||
v++; | v++; | ||||
} | } | ||||
v = baseAccount.setBytes(Bytes.fromString("A"), BytesValueEntry.fromText("VALUE_A_" + v), v + 1); | |||||
v = baseAccount.setBytes(Bytes.fromString("A"), BytesData.fromText("VALUE_A_" + v), v + 1); | |||||
// 预期成功; | // 预期成功; | ||||
assertEquals(-1, v); | assertEquals(-1, v); | ||||
@@ -0,0 +1,50 @@ | |||||
package test.com.jd.blockchain.ledger; | |||||
import java.util.Map; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
import com.jd.blockchain.contract.ContractType; | |||||
import com.jd.blockchain.contract.engine.ContractCode; | |||||
import com.jd.blockchain.contract.jvm.AbstractContractCode; | |||||
import com.jd.blockchain.contract.jvm.ContractDefinition; | |||||
import com.jd.blockchain.ledger.core.ContractAccount; | |||||
import com.jd.blockchain.ledger.core.impl.handles.AbtractContractEventHandle; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
public class ContractInvokingHandle extends AbtractContractEventHandle { | |||||
private Map<Bytes, ContractCode> contractInstances = new ConcurrentHashMap<Bytes, ContractCode>(); | |||||
@Override | |||||
protected ContractCode loadContractCode(ContractAccount contract) { | |||||
return contractInstances.get(contract.getAddress()); | |||||
} | |||||
public <T> ContractCode setup(Bytes address, Class<T> contractIntf, T instance) { | |||||
ContractCodeInstance<T> contract = new ContractCodeInstance<T>(address, 0, contractIntf, instance); | |||||
contractInstances.put(address, contract); | |||||
return contract; | |||||
} | |||||
private static class ContractCodeInstance<T> extends AbstractContractCode { | |||||
private T instance; | |||||
public ContractCodeInstance(Bytes address, long version, Class<T> delaredInterface, T instance) { | |||||
super(address, version, resolveContractDefinition(delaredInterface, instance.getClass())); | |||||
this.instance = instance; | |||||
} | |||||
private static ContractDefinition resolveContractDefinition(Class<?> declaredIntf, Class<?> implementedClass) { | |||||
ContractType contractType = ContractType.resolve(declaredIntf); | |||||
return new ContractDefinition(contractType, implementedClass); | |||||
} | |||||
@Override | |||||
protected T getContractInstance() { | |||||
return instance; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,223 @@ | |||||
package test.com.jd.blockchain.ledger; | |||||
import static org.junit.Assert.assertArrayEquals; | |||||
import static org.junit.Assert.assertEquals; | |||||
import static org.junit.Assert.assertNotNull; | |||||
import static org.junit.Assert.assertNull; | |||||
import static org.mockito.Matchers.anyLong; | |||||
import static org.mockito.Matchers.anyString; | |||||
import static org.mockito.Mockito.times; | |||||
import static org.mockito.Mockito.verify; | |||||
import static org.mockito.Mockito.when; | |||||
import java.util.Random; | |||||
import org.junit.Test; | |||||
import org.mockito.Mockito; | |||||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.ledger.BytesData; | |||||
import com.jd.blockchain.ledger.EndpointRequest; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | |||||
import com.jd.blockchain.ledger.LedgerInitSetting; | |||||
import com.jd.blockchain.ledger.LedgerTransaction; | |||||
import com.jd.blockchain.ledger.NodeRequest; | |||||
import com.jd.blockchain.ledger.OperationResult; | |||||
import com.jd.blockchain.ledger.TransactionContent; | |||||
import com.jd.blockchain.ledger.TransactionContentBody; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | |||||
import com.jd.blockchain.ledger.TransactionRequestBuilder; | |||||
import com.jd.blockchain.ledger.TransactionResponse; | |||||
import com.jd.blockchain.ledger.TransactionState; | |||||
import com.jd.blockchain.ledger.UserRegisterOperation; | |||||
import com.jd.blockchain.ledger.core.LedgerDataSet; | |||||
import com.jd.blockchain.ledger.core.LedgerEditor; | |||||
import com.jd.blockchain.ledger.core.LedgerRepository; | |||||
import com.jd.blockchain.ledger.core.LedgerTransactionContext; | |||||
import com.jd.blockchain.ledger.core.UserAccount; | |||||
import com.jd.blockchain.ledger.core.impl.DefaultOperationHandleRegisteration; | |||||
import com.jd.blockchain.ledger.core.impl.LedgerManager; | |||||
import com.jd.blockchain.ledger.core.impl.LedgerTransactionalEditor; | |||||
import com.jd.blockchain.ledger.core.impl.TransactionBatchProcessor; | |||||
import com.jd.blockchain.service.TransactionBatchResultHandle; | |||||
import com.jd.blockchain.storage.service.utils.MemoryKVStorage; | |||||
import com.jd.blockchain.transaction.TxBuilder; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
public class ContractInvokingTest { | |||||
static { | |||||
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(UserRegisterOperation.class); | |||||
} | |||||
private static final String LEDGER_KEY_PREFIX = "LDG://"; | |||||
private BlockchainKeypair parti0 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair parti1 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); | |||||
// 采用基于内存的 Storage; | |||||
private MemoryKVStorage storage = new MemoryKVStorage(); | |||||
@Test | |||||
public void test() { | |||||
// 初始化账本到指定的存储库; | |||||
HashDigest ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3); | |||||
// 重新加载账本; | |||||
LedgerManager ledgerManager = new LedgerManager(); | |||||
LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage); | |||||
// 创建合约处理器; | |||||
ContractInvokingHandle contractInvokingHandle = new ContractInvokingHandle(); | |||||
// 创建和加载合约实例; | |||||
BlockchainKeypair contractKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
Bytes contractAddress = contractKey.getAddress(); | |||||
TestContract contractInstance = Mockito.mock(TestContract.class); | |||||
contractInvokingHandle.setup(contractAddress, TestContract.class, contractInstance); | |||||
// 注册合约处理器; | |||||
DefaultOperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration(); | |||||
opReg.insertAsTopPriority(contractInvokingHandle); | |||||
// 发布指定地址合约 | |||||
deploy(ledgerRepo, ledgerManager, opReg, ledgerHash, contractKey); | |||||
// 创建新区块的交易处理器; | |||||
LedgerBlock preBlock = ledgerRepo.getLatestBlock(); | |||||
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock); | |||||
// 加载合约 | |||||
LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); | |||||
TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, | |||||
opReg, ledgerManager); | |||||
// 构建基于接口调用合约的交易请求,用于测试合约调用; | |||||
TxBuilder txBuilder = new TxBuilder(ledgerHash); | |||||
TestContract contractProxy = txBuilder.contract(contractAddress, TestContract.class); | |||||
TestContract contractProxy1 = txBuilder.contract(contractAddress, TestContract.class); | |||||
String asset = "AK"; | |||||
long issueAmount = new Random().nextLong(); | |||||
when(contractInstance.issue(anyString(), anyLong())).thenReturn(issueAmount); | |||||
contractProxy.issue(asset, issueAmount); | |||||
TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||||
txReqBuilder.signAsEndpoint(parti0); | |||||
txReqBuilder.signAsNode(parti0); | |||||
TransactionRequest txReq = txReqBuilder.buildRequest(); | |||||
TransactionResponse resp = txbatchProcessor.schedule(txReq); | |||||
verify(contractInstance, times(1)).issue(asset, issueAmount); | |||||
OperationResult[] opResults = resp.getOperationResults(); | |||||
assertEquals(1, opResults.length); | |||||
assertEquals(0, opResults[0].getIndex()); | |||||
byte[] expectedRetnBytes = BinaryProtocol.encode(BytesData.fromInt64(issueAmount), BytesValue.class); | |||||
byte[] reallyRetnBytes = BinaryProtocol.encode(opResults[0].getResult(), BytesValue.class); | |||||
assertArrayEquals(expectedRetnBytes, reallyRetnBytes); | |||||
// 提交区块; | |||||
TransactionBatchResultHandle txResultHandle = txbatchProcessor.prepare(); | |||||
txResultHandle.commit(); | |||||
LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | |||||
assertEquals(preBlock.getHeight() + 1, latestBlock.getHeight()); | |||||
assertEquals(resp.getBlockHeight(), latestBlock.getHeight()); | |||||
assertEquals(resp.getBlockHash(), latestBlock.getHash()); | |||||
// 再验证一次结果; | |||||
assertEquals(1, opResults.length); | |||||
assertEquals(0, opResults[0].getIndex()); | |||||
reallyRetnBytes = BinaryProtocol.encode(opResults[0].getResult(), BytesValue.class); | |||||
assertArrayEquals(expectedRetnBytes, reallyRetnBytes); | |||||
} | |||||
private void deploy(LedgerRepository ledgerRepo, LedgerManager ledgerManager, | |||||
DefaultOperationHandleRegisteration opReg, HashDigest ledgerHash, | |||||
BlockchainKeypair contractKey) { | |||||
// 创建新区块的交易处理器; | |||||
LedgerBlock preBlock = ledgerRepo.getLatestBlock(); | |||||
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock); | |||||
// 加载合约 | |||||
LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); | |||||
TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, | |||||
opReg, ledgerManager); | |||||
// 构建基于接口调用合约的交易请求,用于测试合约调用; | |||||
TxBuilder txBuilder = new TxBuilder(ledgerHash); | |||||
txBuilder.contracts().deploy(contractKey.getIdentity(), chainCode()); | |||||
TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||||
txReqBuilder.signAsEndpoint(parti0); | |||||
txReqBuilder.signAsNode(parti0); | |||||
TransactionRequest txReq = txReqBuilder.buildRequest(); | |||||
TransactionResponse resp = txbatchProcessor.schedule(txReq); | |||||
OperationResult[] opResults = resp.getOperationResults(); | |||||
assertNull(opResults); | |||||
// 提交区块; | |||||
TransactionBatchResultHandle txResultHandle = txbatchProcessor.prepare(); | |||||
txResultHandle.commit(); | |||||
} | |||||
private HashDigest initLedger(MemoryKVStorage storage, BlockchainKeypair... partiKeys) { | |||||
// 创建初始化配置; | |||||
LedgerInitSetting initSetting = LedgerTestUtils.createLedgerInitSetting(partiKeys); | |||||
// 创建账本; | |||||
LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | |||||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||||
LedgerTransactionContext genisisTxCtx = ldgEdt.newTransaction(genesisTxReq); | |||||
LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | |||||
for (int i = 0; i < partiKeys.length; i++) { | |||||
UserAccount userAccount = ldgDS.getUserAccountSet().register(partiKeys[i].getAddress(), | |||||
partiKeys[i].getPubKey()); | |||||
userAccount.setProperty("Name", "参与方-" + i, -1); | |||||
userAccount.setProperty("Share", "" + (10 + i), -1); | |||||
} | |||||
LedgerTransaction tx = genisisTxCtx.commit(TransactionState.SUCCESS); | |||||
assertEquals(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); | |||||
assertEquals(0, tx.getBlockHeight()); | |||||
LedgerBlock block = ldgEdt.prepare(); | |||||
assertEquals(0, block.getHeight()); | |||||
assertNotNull(block.getHash()); | |||||
assertNull(block.getPreviousHash()); | |||||
assertEquals(block.getHash(), block.getLedgerHash()); | |||||
// 提交数据,写入存储; | |||||
ldgEdt.commit(); | |||||
HashDigest ledgerHash = block.getHash(); | |||||
return ledgerHash; | |||||
} | |||||
private byte[] chainCode() { | |||||
byte[] chainCode = new byte[1024]; | |||||
new Random().nextBytes(chainCode); | |||||
return chainCode; | |||||
} | |||||
} |
@@ -18,7 +18,7 @@ import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | |||||
import com.jd.blockchain.crypto.service.sm.SMCryptoService; | import com.jd.blockchain.crypto.service.sm.SMCryptoService; | ||||
import com.jd.blockchain.ledger.BlockchainKeypair; | import com.jd.blockchain.ledger.BlockchainKeypair; | ||||
import com.jd.blockchain.ledger.BytesValue; | import com.jd.blockchain.ledger.BytesValue; | ||||
import com.jd.blockchain.ledger.BytesValueType; | |||||
import com.jd.blockchain.ledger.DataType; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | import com.jd.blockchain.ledger.LedgerBlock; | ||||
import com.jd.blockchain.ledger.LedgerInitSetting; | import com.jd.blockchain.ledger.LedgerInitSetting; | ||||
import com.jd.blockchain.ledger.LedgerTransaction; | import com.jd.blockchain.ledger.LedgerTransaction; | ||||
@@ -68,17 +68,17 @@ public class LedgerEditerTest { | |||||
MemoryKVStorage storage = new MemoryKVStorage(); | MemoryKVStorage storage = new MemoryKVStorage(); | ||||
// 创建初始化配置; | // 创建初始化配置; | ||||
LedgerInitSetting initSetting = createLedgerInitSetting(); | |||||
LedgerInitSetting initSetting = LedgerTestUtils.createLedgerInitSetting(); | |||||
// 创建账本; | // 创建账本; | ||||
return LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | return LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | ||||
} | } | ||||
private LedgerTransactionContext createGenisisTx(LedgerEditor ldgEdt) { | private LedgerTransactionContext createGenisisTx(LedgerEditor ldgEdt) { | ||||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest(null, signatureFunction); | |||||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||||
LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | ||||
return txCtx; | return txCtx; | ||||
} | } | ||||
@@ -88,8 +88,7 @@ public class LedgerEditerTest { | |||||
LedgerEditor ldgEdt = createLedgerInitEditor(); | LedgerEditor ldgEdt = createLedgerInitEditor(); | ||||
LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt); | LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt); | ||||
LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | ||||
AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | ||||
BlockchainKeypair dataKP = new BlockchainKeypair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); | BlockchainKeypair dataKP = new BlockchainKeypair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); | ||||
@@ -109,7 +108,7 @@ public class LedgerEditerTest { | |||||
// 验证数据读写的一致性; | // 验证数据读写的一致性; | ||||
BytesValue bytes = dataAccount.getBytes("A"); | BytesValue bytes = dataAccount.getBytes("A"); | ||||
assertEquals(BytesValueType.TEXT, bytes.getType()); | |||||
assertEquals(DataType.TEXT, bytes.getType()); | |||||
String textValue = bytes.getValue().toUTF8String(); | String textValue = bytes.getValue().toUTF8String(); | ||||
assertEquals("abc", textValue); | assertEquals("abc", textValue); | ||||
} | } | ||||
@@ -147,42 +146,5 @@ public class LedgerEditerTest { | |||||
ldgEdt.commit(); | ldgEdt.commit(); | ||||
} | } | ||||
private LedgerInitSetting createLedgerInitSetting() { | |||||
SignatureFunction signFunc = Crypto.getSignatureFunction("ED25519"); | |||||
CryptoProvider[] supportedProviders = new CryptoProvider[SUPPORTED_PROVIDERS.length]; | |||||
for (int i = 0; i < SUPPORTED_PROVIDERS.length; i++) { | |||||
supportedProviders[i] = Crypto.getProvider(SUPPORTED_PROVIDERS[i]); | |||||
} | |||||
CryptoConfig defCryptoSetting = new CryptoConfig(); | |||||
defCryptoSetting.setSupportedProviders(supportedProviders); | |||||
defCryptoSetting.setAutoVerifyHash(true); | |||||
defCryptoSetting.setHashAlgorithm(ClassicAlgorithm.SHA256); | |||||
LedgerInitSettingData initSetting = new LedgerInitSettingData(); | |||||
initSetting.setLedgerSeed(BytesUtils.toBytes("A Test Ledger seed!", "UTF-8")); | |||||
initSetting.setCryptoSetting(defCryptoSetting); | |||||
ConsensusParticipantData[] parties = new ConsensusParticipantData[2]; | |||||
parties[0] = new ConsensusParticipantData(); | |||||
parties[0].setId(0); | |||||
parties[0].setName("John"); | |||||
AsymmetricKeypair kp0 = signFunc.generateKeypair(); | |||||
parties[0].setPubKey(kp0.getPubKey()); | |||||
parties[0].setAddress(AddressEncoding.generateAddress(kp0.getPubKey()).toBase58()); | |||||
parties[0].setHostAddress(new NetworkAddress("192.168.1.6", 9000)); | |||||
parties[1] = new ConsensusParticipantData(); | |||||
parties[1].setId(1); | |||||
parties[1].setName("John"); | |||||
AsymmetricKeypair kp1 = signFunc.generateKeypair(); | |||||
parties[1].setPubKey(kp1.getPubKey()); | |||||
parties[1].setAddress(AddressEncoding.generateAddress(kp1.getPubKey()).toBase58()); | |||||
parties[1].setHostAddress(new NetworkAddress("192.168.1.7", 9000)); | |||||
initSetting.setConsensusParticipants(parties); | |||||
return initSetting; | |||||
} | |||||
} | } |
@@ -83,13 +83,13 @@ public class LedgerManagerTest { | |||||
LedgerEditor ldgEdt = ledgerManager.newLedger(initSetting, storage); | LedgerEditor ldgEdt = ledgerManager.newLedger(initSetting, storage); | ||||
// 创建一个模拟的创世交易; | // 创建一个模拟的创世交易; | ||||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest(null, signatureFunction); | |||||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||||
// 记录交易,注册用户; | // 记录交易,注册用户; | ||||
LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | ||||
LedgerDataSet ldgDS = txCtx.getDataSet(); | LedgerDataSet ldgDS = txCtx.getDataSet(); | ||||
BlockchainKeypair userKP = BlockchainKeyGenerator.getInstance().generate(); | BlockchainKeypair userKP = BlockchainKeyGenerator.getInstance().generate(); | ||||
; | |||||
UserAccount userAccount = ldgDS.getUserAccountSet().register(userKP.getAddress(), userKP.getPubKey()); | UserAccount userAccount = ldgDS.getUserAccountSet().register(userKP.getAddress(), userKP.getPubKey()); | ||||
userAccount.setProperty("Name", "孙悟空", -1); | userAccount.setProperty("Name", "孙悟空", -1); | ||||
userAccount.setProperty("Age", "10000", -1); | userAccount.setProperty("Age", "10000", -1); | ||||
@@ -2,69 +2,103 @@ package test.com.jd.blockchain.ledger; | |||||
import java.util.Random; | import java.util.Random; | ||||
import com.jd.blockchain.crypto.AsymmetricKeypair; | |||||
import com.jd.blockchain.crypto.AddressEncoding; | |||||
import com.jd.blockchain.crypto.Crypto; | import com.jd.blockchain.crypto.Crypto; | ||||
import com.jd.blockchain.crypto.CryptoAlgorithm; | |||||
import com.jd.blockchain.crypto.CryptoProvider; | import com.jd.blockchain.crypto.CryptoProvider; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.crypto.PubKey; | |||||
import com.jd.blockchain.crypto.SignatureFunction; | import com.jd.blockchain.crypto.SignatureFunction; | ||||
import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; | import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; | ||||
import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | ||||
import com.jd.blockchain.crypto.service.sm.SMCryptoService; | import com.jd.blockchain.crypto.service.sm.SMCryptoService; | ||||
import com.jd.blockchain.ledger.BlockchainIdentityData; | |||||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||||
import com.jd.blockchain.ledger.CryptoSetting; | import com.jd.blockchain.ledger.CryptoSetting; | ||||
import com.jd.blockchain.ledger.PreparedTransaction; | |||||
import com.jd.blockchain.ledger.LedgerInitSetting; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
import com.jd.blockchain.ledger.TransactionRequestBuilder; | |||||
import com.jd.blockchain.ledger.TransactionResponse; | import com.jd.blockchain.ledger.TransactionResponse; | ||||
import com.jd.blockchain.ledger.core.CryptoConfig; | import com.jd.blockchain.ledger.core.CryptoConfig; | ||||
import com.jd.blockchain.ledger.core.impl.TransactionStagedSnapshot; | import com.jd.blockchain.ledger.core.impl.TransactionStagedSnapshot; | ||||
import com.jd.blockchain.transaction.ConsensusParticipantData; | |||||
import com.jd.blockchain.transaction.LedgerInitSettingData; | |||||
import com.jd.blockchain.transaction.TransactionService; | import com.jd.blockchain.transaction.TransactionService; | ||||
import com.jd.blockchain.transaction.TxTemplate; | |||||
import com.jd.blockchain.transaction.TxBuilder; | |||||
import com.jd.blockchain.utils.io.BytesUtils; | |||||
import com.jd.blockchain.utils.net.NetworkAddress; | |||||
public class LedgerTestUtils { | public class LedgerTestUtils { | ||||
// private static ThreadLocalRandom rand = ThreadLocalRandom.current(); | |||||
public static final SignatureFunction ED25519_SIGN_FUNC = Crypto.getSignatureFunction("ED25519"); | |||||
public static final CryptoAlgorithm ED25519 = ED25519_SIGN_FUNC.getAlgorithm(); | |||||
private static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(), | private static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(), | ||||
SMCryptoService.class.getName() }; | SMCryptoService.class.getName() }; | ||||
private static Random rand = new Random(); | private static Random rand = new Random(); | ||||
public static TransactionRequest createTxRequest(HashDigest ledgerHash) { | |||||
SignatureFunction signFunc = Crypto.getSignatureFunction("ED25519"); | |||||
return createTxRequest(ledgerHash, signFunc); | |||||
public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash) { | |||||
BlockchainKeypair key = BlockchainKeyGenerator.getInstance().generate(ED25519); | |||||
return createTxRequest_UserReg(ledgerHash, key); | |||||
} | |||||
public static LedgerInitSetting createLedgerInitSetting() { | |||||
BlockchainKeypair[] partiKeys = new BlockchainKeypair[2]; | |||||
partiKeys[0] = BlockchainKeyGenerator.getInstance().generate(); | |||||
partiKeys[1] = BlockchainKeyGenerator.getInstance().generate(); | |||||
return createLedgerInitSetting(partiKeys); | |||||
} | } | ||||
public static TransactionRequest createTxRequest(HashDigest ledgerHash, SignatureFunction signatureFunction) { | |||||
TxHandle txHandle = new TxHandle(); | |||||
public static LedgerInitSetting createLedgerInitSetting(BlockchainKeypair[] partiKeys) { | |||||
CryptoProvider[] supportedProviders = new CryptoProvider[SUPPORTED_PROVIDERS.length]; | |||||
for (int i = 0; i < SUPPORTED_PROVIDERS.length; i++) { | |||||
supportedProviders[i] = Crypto.getProvider(SUPPORTED_PROVIDERS[i]); | |||||
} | |||||
CryptoConfig defCryptoSetting = new CryptoConfig(); | |||||
defCryptoSetting.setSupportedProviders(supportedProviders); | |||||
defCryptoSetting.setAutoVerifyHash(true); | |||||
defCryptoSetting.setHashAlgorithm(ClassicAlgorithm.SHA256); | |||||
LedgerInitSettingData initSetting = new LedgerInitSettingData(); | |||||
TxTemplate txTemp = new TxTemplate(ledgerHash, txHandle); | |||||
initSetting.setLedgerSeed(BytesUtils.toBytes("A Test Ledger seed!", "UTF-8")); | |||||
initSetting.setCryptoSetting(defCryptoSetting); | |||||
ConsensusParticipantData[] parties = new ConsensusParticipantData[partiKeys.length]; | |||||
for (int i = 0; i < parties.length; i++) { | |||||
parties[i] = new ConsensusParticipantData(); | |||||
parties[i].setId(0); | |||||
parties[i].setName("Parti-" + i); | |||||
parties[i].setPubKey(partiKeys[i].getPubKey()); | |||||
parties[i].setAddress(AddressEncoding.generateAddress(partiKeys[i].getPubKey()).toBase58()); | |||||
parties[i].setHostAddress(new NetworkAddress("192.168.1." + (10 + i), 9000)); | |||||
AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | |||||
PubKey pubKey = cryptoKeyPair.getPubKey(); | |||||
txTemp.users().register(new BlockchainIdentityData(pubKey)); | |||||
PreparedTransaction ptx = txTemp.prepare(); | |||||
ptx.sign(cryptoKeyPair); | |||||
ptx.commit(); | |||||
return txHandle.txRequest; | |||||
} | |||||
initSetting.setConsensusParticipants(parties); | |||||
return initSetting; | |||||
} | |||||
public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash, BlockchainKeypair userKeypair) { | |||||
return createTxRequest_UserReg(ledgerHash, userKeypair, null); | |||||
} | } | ||||
// public static TransactionRequest createContractEventTxRequest(HashDigest ledgerHash, | |||||
// SignatureFunction signatureFunction, String contractAddress, String event, byte[] args) { | |||||
// TxHandle txHandle = new TxHandle(); | |||||
// | |||||
// TxTemplate txTemp = new TxTemplate(ledgerHash, txHandle); | |||||
// | |||||
// txTemp.contractEvents().send(contractAddress, event, args); | |||||
// | |||||
// AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | |||||
// PubKey pubKey = cryptoKeyPair.getPubKey(); | |||||
// txTemp.users().register(new BlockchainIdentityData(pubKey)); | |||||
// PreparedTransaction ptx = txTemp.prepare(); | |||||
// ptx.sign(cryptoKeyPair); | |||||
// ptx.commit(); | |||||
// return txHandle.txRequest; | |||||
// } | |||||
public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash, BlockchainKeypair userKeypair, | |||||
BlockchainKeypair gatewayKeypair) { | |||||
TxBuilder txBuilder = new TxBuilder(ledgerHash); | |||||
txBuilder.users().register(userKeypair.getIdentity()); | |||||
TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||||
txReqBuilder.signAsEndpoint(userKeypair); | |||||
if (gatewayKeypair != null) { | |||||
txReqBuilder.signAsNode(gatewayKeypair); | |||||
} | |||||
return txReqBuilder.buildRequest(); | |||||
} | |||||
public static TransactionStagedSnapshot generateRandomSnapshot() { | public static TransactionStagedSnapshot generateRandomSnapshot() { | ||||
TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); | TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); | ||||
@@ -68,7 +68,7 @@ public class LedgerTransactionDataTest { | |||||
long blockHeight = 9986L; | long blockHeight = 9986L; | ||||
data = new LedgerTransactionData(blockHeight, txRequestMessage, TransactionState.SUCCESS, | data = new LedgerTransactionData(blockHeight, txRequestMessage, TransactionState.SUCCESS, | ||||
initTransactionStagedSnapshot()); | |||||
initTransactionStagedSnapshot(), null); | |||||
HashDigest hash = new HashDigest(ClassicAlgorithm.SHA256, "zhangsan".getBytes()); | HashDigest hash = new HashDigest(ClassicAlgorithm.SHA256, "zhangsan".getBytes()); | ||||
HashDigest adminAccountHash = new HashDigest(ClassicAlgorithm.SHA256, "lisi".getBytes()); | HashDigest adminAccountHash = new HashDigest(ClassicAlgorithm.SHA256, "lisi".getBytes()); | ||||
@@ -0,0 +1,58 @@ | |||||
package test.com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.contract.Contract; | |||||
import com.jd.blockchain.contract.ContractEvent; | |||||
@Contract | |||||
public interface TestContract { | |||||
/** | |||||
* 发行资产; | |||||
* | |||||
* @param asset 资产代码; | |||||
* @param amount 本次发行的资产数量; | |||||
* @return 资产总量; | |||||
*/ | |||||
@ContractEvent(name = "issue") | |||||
long issue(String asset, long amount); | |||||
/** | |||||
* 获取资产总量; | |||||
* | |||||
* @param asset | |||||
* @return | |||||
*/ | |||||
@ContractEvent(name = "get-amount") | |||||
long getAmount(String asset); | |||||
/** | |||||
* 获取资产余额; | |||||
* | |||||
* @param address | |||||
* @param asset | |||||
* @return | |||||
*/ | |||||
@ContractEvent(name = "get-balance") | |||||
long getBalance(String address, String asset); | |||||
/** | |||||
* 向账户分配资产; | |||||
* | |||||
* @param address | |||||
* @param asset | |||||
* @param amount | |||||
*/ | |||||
@ContractEvent(name = "assign") | |||||
void assign(String address, String asset, int amount); | |||||
/** | |||||
* 转移资产; | |||||
* | |||||
* @param fromAddress | |||||
* @param toAddress | |||||
* @param asset | |||||
* @param amount | |||||
*/ | |||||
@ContractEvent(name = "transfer") | |||||
void transfer(String fromAddress, String toAddress, String asset, long amount); | |||||
} |
@@ -0,0 +1,49 @@ | |||||
package test.com.jd.blockchain.ledger; | |||||
public interface TestContractImpl { | |||||
/** | |||||
* 发行资产; | |||||
* | |||||
* @param asset 资产代码; | |||||
* @param amount 本次发行的资产数量; | |||||
* @return 资产总量; | |||||
*/ | |||||
long issue(String asset, long amount); | |||||
/** | |||||
* 获取资产总量; | |||||
* | |||||
* @param asset | |||||
* @return | |||||
*/ | |||||
long getAmount(String asset); | |||||
/** | |||||
* 获取资产余额; | |||||
* | |||||
* @param address | |||||
* @param asset | |||||
* @return | |||||
*/ | |||||
long getBalance(String address, String asset); | |||||
/** | |||||
* 向账户分配资产; | |||||
* | |||||
* @param address | |||||
* @param asset | |||||
* @param amount | |||||
*/ | |||||
void assign(String address, String asset, int amount); | |||||
/** | |||||
* 转移资产; | |||||
* | |||||
* @param fromAddress | |||||
* @param toAddress | |||||
* @param asset | |||||
* @param amount | |||||
*/ | |||||
void transfer(String fromAddress, String toAddress, String asset, long amount); | |||||
} |
@@ -1,392 +1,140 @@ | |||||
//package test.com.jd.blockchain.ledger; | |||||
// | |||||
//import static org.junit.Assert.assertArrayEquals; | |||||
//import static org.junit.Assert.assertEquals; | |||||
//import static org.junit.Assert.assertNotNull; | |||||
//import static org.junit.Assert.assertTrue; | |||||
// | |||||
//import java.io.File; | |||||
//import java.io.FileInputStream; | |||||
//import java.io.IOException; | |||||
//import java.io.InputStream; | |||||
//import java.net.URISyntaxException; | |||||
//import java.net.URL; | |||||
//import java.nio.file.Paths; | |||||
// | |||||
//import org.junit.Before; | |||||
//import org.junit.Test; | |||||
// | |||||
//import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
//import com.jd.blockchain.contract.model.ContractPath; | |||||
//import com.jd.blockchain.crypto.CryptoAlgorithm; | |||||
//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.hash.HashDigest; | |||||
//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.LedgerBlock; | |||||
//import com.jd.blockchain.ledger.LedgerInitSetting; | |||||
//import com.jd.blockchain.ledger.LedgerTransaction; | |||||
//import com.jd.blockchain.ledger.NodeRequest; | |||||
//import com.jd.blockchain.ledger.TransactionBuilder; | |||||
//import com.jd.blockchain.ledger.TransactionContent; | |||||
//import com.jd.blockchain.ledger.TransactionContentBody; | |||||
//import com.jd.blockchain.ledger.TransactionRequest; | |||||
//import com.jd.blockchain.ledger.TransactionRequestBuilder; | |||||
//import com.jd.blockchain.ledger.TransactionResponse; | |||||
//import com.jd.blockchain.ledger.TransactionState; | |||||
//import com.jd.blockchain.ledger.UserRegisterOperation; | |||||
//import com.jd.blockchain.ledger.core.CryptoConfig; | |||||
//import com.jd.blockchain.ledger.core.DataAccountSet; | |||||
//import com.jd.blockchain.ledger.core.LedgerDataSet; | |||||
//import com.jd.blockchain.ledger.core.LedgerEditor; | |||||
//import com.jd.blockchain.ledger.core.LedgerRepository; | |||||
//import com.jd.blockchain.ledger.core.LedgerTransactionContext; | |||||
//import com.jd.blockchain.ledger.core.TransactionSet; | |||||
//import com.jd.blockchain.ledger.core.UserAccount; | |||||
//import com.jd.blockchain.ledger.core.impl.DefaultOperationHandleRegisteration; | |||||
//import com.jd.blockchain.ledger.core.impl.LedgerManager; | |||||
//import com.jd.blockchain.ledger.core.impl.OperationHandleRegisteration; | |||||
//import com.jd.blockchain.ledger.core.impl.TransactionBatchProcessor; | |||||
//import com.jd.blockchain.ledger.data.AddressEncoding; | |||||
//import com.jd.blockchain.ledger.data.ConsensusParticipantData; | |||||
//import com.jd.blockchain.ledger.data.LedgerInitSettingData; | |||||
//import com.jd.blockchain.ledger.data.TxBuilder; | |||||
//import com.jd.blockchain.storage.service.utils.MemoryKVStorage; | |||||
// | |||||
//import my.utils.Bytes; | |||||
//import my.utils.io.BytesUtils; | |||||
//import my.utils.net.NetworkAddress; | |||||
// | |||||
//public class TransactionBatchProcessorTest { | |||||
// private HashDigest ledgerHash = null; | |||||
// BlockchainKeyPair userKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
// BlockchainKeyPair dataKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
// BlockchainKeyPair sponsorKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
// BlockchainKeyPair gatewayKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
// BlockchainKeyPair contractKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
// TransactionRequest transactionRequest; | |||||
// String pubKeyVal = "jd.com"+Thread.currentThread(); | |||||
//// String userPubKeyVal = "this is user's pubKey"; | |||||
// byte[] chainCode; | |||||
// // 保存资产总数的键; | |||||
// private static final String KEY_TOTAL = "TOTAL"; | |||||
// //第二个参数; | |||||
// private static final String KEY_ABC = "abc"; | |||||
// // 采用基于内存的 Storage; | |||||
// MemoryKVStorage storage = new MemoryKVStorage(); | |||||
// | |||||
// @Before | |||||
// public void setUp(){ | |||||
// 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(UserRegisterOperation.class); | |||||
// } | |||||
// | |||||
//// @After | |||||
// public void after() { | |||||
// //清理所有使用的临时文件; | |||||
// String outputPath = null; | |||||
// try { | |||||
// outputPath = ContractPath.getOutputPath(); | |||||
// }finally { | |||||
// deleteDir(new File(outputPath)); | |||||
// } | |||||
// } | |||||
// | |||||
// /** | |||||
// * 递归删除目录下的所有文件及子目录下所有文件 | |||||
// * @param dir 将要删除的文件目录 | |||||
// * @return boolean Returns "true" if all deletions were successful. | |||||
// * If a deletion fails, the method stops attempting to | |||||
// * delete and returns "false". | |||||
// */ | |||||
// private static boolean deleteDir(File dir) { | |||||
// if (dir.isDirectory()) { | |||||
// String[] children = dir.list(); | |||||
// //递归删除目录中的子目录下 | |||||
// for (int i=0; i<children.length; i++) { | |||||
// boolean success = deleteDir(new File(dir, children[i])); | |||||
// if (!success) { | |||||
// return false; | |||||
// } | |||||
// } | |||||
// } | |||||
// // 目录此时为空,可以删除 | |||||
// return dir.delete(); | |||||
// } | |||||
// | |||||
// @Test | |||||
// public void testContractOperations() { | |||||
// LedgerManager ledgerManager = new LedgerManager(); | |||||
// | |||||
// ledgerHash = initLedger(ledgerManager, storage, sponsorKey,gatewayKey,contractKey); | |||||
// LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage); | |||||
// | |||||
// LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | |||||
// UserAccount userAcc = previousBlockDataset.getUserAccountSet().getUser(sponsorKey.getAddress()); | |||||
// assertNotNull(userAcc); | |||||
// boolean sponsorRegistered = previousBlockDataset.getUserAccountSet().contains(sponsorKey.getAddress()); | |||||
// assertTrue(sponsorRegistered); | |||||
// | |||||
// LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); | |||||
// | |||||
// OperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration(); | |||||
// TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, | |||||
// opReg, ledgerManager); | |||||
// | |||||
// // TODO: 生成合约交易;以及执行合约交易; | |||||
// transactionRequest = this.contractDeploy(ledgerHash, userKey, dataKey,sponsorKey,gatewayKey,contractKey); | |||||
// txbatchProcessor.schedule(transactionRequest); | |||||
// | |||||
// LedgerBlock newBlock = newBlockEditor.prepare(); | |||||
// newBlockEditor.commit(); | |||||
// | |||||
// //验证正确性; | |||||
// ledgerManager = new LedgerManager(); | |||||
// ledgerRepo = ledgerManager.register(ledgerHash, storage); | |||||
// | |||||
// //验证合约部署 | |||||
// byte[] actualChainCode = ledgerRepo.getContractAccountSet(newBlock).getContract(contractKey.getAddress()).getChainCode(); | |||||
// assertArrayEquals(chainCode, actualChainCode); | |||||
// | |||||
// LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | |||||
// assertEquals(newBlock.getHash(), latestBlock.getHash()); | |||||
// | |||||
// //执行事件; | |||||
// LedgerEditor newBlockEditor1 = ledgerRepo.createNextBlock(); | |||||
// LedgerDataSet previousBlockDataset1 = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | |||||
//// 采用schedule方式; | |||||
// txbatchProcessor = new TransactionBatchProcessor(newBlockEditor1, previousBlockDataset1, | |||||
// opReg, ledgerManager); | |||||
// txbatchProcessor.schedule(this.exeContractReq(ledgerHash, sponsorKey,gatewayKey,contractKey)); | |||||
// //采用newTransaction方式处理; | |||||
// /** | |||||
// LedgerTransactionContext txCtx = newBlockEditor1.newTransaction( | |||||
// this.exeContractReq(ledgerHash, sponsorKey,gatewayKey,contractKey)); | |||||
// LedgerDataSet ldgDS = txCtx.getDataSet(); | |||||
// SignatureDigest signDigest = CryptoUtils.sign(CryptoAlgorithm.ED25519).sign(contractKey.getPrivKey(), Base58Utils.decode(contractKey.getAddress())); | |||||
// DigitalSignatureBlob dgtSign = new DigitalSignatureBlob(contractKey.getPubKey(), signDigest); | |||||
// // DataAccount dataAccount = ldgDS.getDataAccountSet().register(dataKey.getAddress(), dataKey.getPubKey(),null); | |||||
// // dataAccount.setBytes("latestBlockHash", ledgerRepo.getLatestBlock().getPreviousHash().getRawDigest(), -1); | |||||
// // 提交交易结果; | |||||
// LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS); | |||||
// */ | |||||
// | |||||
// LedgerBlock newBlock1 = newBlockEditor1.prepare(); | |||||
// newBlockEditor1.commit(); | |||||
//// assertEquals(expected, actual); | |||||
// | |||||
// //验证合约中的赋值,外部可以获得; | |||||
// //base test; | |||||
// DataAccountSet dataAccountSet = ledgerRepo.getDataAccountSet(newBlock1); | |||||
// PubKey pubKey = new PubKey(CryptoAlgorithm.ED25519, pubKeyVal.getBytes()); | |||||
// Bytes dataAddress = AddressEncoding.generateAddress(pubKey); | |||||
// assertEquals(dataAddress,dataAccountSet.getDataAccount(dataAddress).getAddress()); | |||||
// assertEquals("hello",new String(dataAccountSet.getDataAccount(dataAddress).getBytes(KEY_TOTAL,-1))); | |||||
// //验证userAccount,从合约内部赋值,然后外部验证; | |||||
//// UserAccountSet userAccountSet = ledgerRepo.getUserAccountSet(newBlock1); | |||||
//// PubKey userPubKey = new PubKey(CryptoAlgorithm.ED25519, userPubKeyVal.getBytes()); | |||||
//// String userAddress = AddressEncoding.generateAddress(userPubKey); | |||||
//// assertEquals(userAddress,userAccountSet.getUser(userAddress).getAddress()); | |||||
// | |||||
// } | |||||
// | |||||
// private TransactionRequest contractDeploy(HashDigest ledgerHash, BlockchainKeyPair userKey, BlockchainKeyPair dataKey, | |||||
// BlockchainKeyPair sponsorKey, BlockchainKeyPair gatewayKey, BlockchainKeyPair contractKey){ | |||||
// // Build transaction request; | |||||
// TransactionBuilder txBuilder = new TxBuilder(ledgerHash); | |||||
// UserRegisterOperation userRegOp = txBuilder.users().register(userKey.getIdentity()); | |||||
// DataAccountRegisterOperation dataAccRegOp = txBuilder.dataAccounts().register(dataKey.getIdentity()); | |||||
// | |||||
// DataAccountKVSetOperation kvsetOP = txBuilder.dataAccount(dataKey.getAddress()) | |||||
// .set("A", "Value_A_0".getBytes(), -1) | |||||
// .set("B", "Value_B_0".getBytes(), -1) | |||||
// .set(KEY_TOTAL, "total value,dataAccount".getBytes(), -1) | |||||
// .set(KEY_ABC, "abc value,dataAccount".getBytes(), -1) | |||||
// //所有的模拟数据都在这个dataAccount中填充; | |||||
// .set("ledgerHash", ledgerHash.getRawDigest(), -1) | |||||
//// .set("latestBlockHash", ledgerBlock.getPreviousHash().getRawDigest(), -1) | |||||
// .getOperation(); | |||||
// chainCode = this.getChainCodeBytes(); | |||||
// ContractCodeDeployOperation contractDplOP = txBuilder.contracts().deploy(contractKey.getIdentity(), this.getChainCodeBytes()); | |||||
//// ContractEventSendOperation contractEvtSendOP = txBuilder.contractEvents().send(contractKey.getAddress(), "test", | |||||
//// "TestContractArgs".getBytes()); | |||||
// TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||||
// txReqBuilder.signAsEndpoint(sponsorKey); | |||||
// txReqBuilder.signAsNode(gatewayKey); | |||||
// return txReqBuilder.buildRequest(); | |||||
// } | |||||
// | |||||
// private TransactionRequest exeContractReq(HashDigest ledgerHash, BlockchainKeyPair sponsorKey, BlockchainKeyPair gatewayKey, BlockchainKeyPair contractKey){ | |||||
// // Build transaction request; | |||||
// TransactionBuilder txBuilder = new TxBuilder(ledgerHash); | |||||
// LedgerManager ledgerManager = new LedgerManager(); | |||||
// LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage); | |||||
// LedgerBlock ledgerBlock = ledgerRepo.getLatestBlock(); | |||||
//// txBuilder.dataAccounts().register(dataKey1.getIdentity()); | |||||
//// txBuilder.dataAccount(dataKey.getAddress()) | |||||
//// .set("latestBlockHash", ledgerBlock.getPreviousHash().getRawDigest(), -1).getOperation(); | |||||
//// HashDigest txRootHashDigest = ledgerRepo.getTransactionSet(ledgerBlock).getRootHash(); | |||||
// | |||||
// LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | |||||
// previousBlockDataset.getUserAccountSet().getUser(userKey.getAddress()); | |||||
// | |||||
// | |||||
// ContractEventSendOperation contractEvtSendOP = txBuilder.contractEvents().send(contractKey.getAddress(), "issue-asset", | |||||
// ("888##abc##"+dataKey.getAddress()+"##"+ledgerBlock.getPreviousHash().toBase58()+"##"+ | |||||
// userKey.getAddress()+"##"+contractKey.getAddress()+"##"+transactionRequest.getTransactionContent().getHash().toBase58() | |||||
// +"##"+pubKeyVal).getBytes()); | |||||
// | |||||
// TransactionSet txset = ledgerRepo.getTransactionSet(ledgerBlock); | |||||
// txset.get(transactionRequest.getTransactionContent().getHash());//此处有value; | |||||
// | |||||
// TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||||
// txReqBuilder.signAsEndpoint(sponsorKey); | |||||
// txReqBuilder.signAsNode(gatewayKey); | |||||
// return txReqBuilder.buildRequest(); | |||||
// } | |||||
// | |||||
// private HashDigest initLedger(LedgerManager ledgerManager, MemoryKVStorage storage, BlockchainKeyPair userKP, | |||||
// BlockchainKeyPair gatewayKey,BlockchainKeyPair contractKey) { | |||||
// // 创建账本初始化配置; | |||||
// LedgerInitSetting initSetting = createLedgerInitSetting(); | |||||
// | |||||
// // 新建账本; | |||||
// LedgerEditor ldgEdt = ledgerManager.newLedger(initSetting, storage); | |||||
// | |||||
// // 创建一个模拟的创世交易; | |||||
// TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest(ledgerHash, | |||||
// CryptoUtils.sign(CryptoAlgorithm.ED25519)); | |||||
// | |||||
// // 记录交易,注册用户; | |||||
// LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | |||||
// LedgerDataSet ldgDS = txCtx.getDataSet(); | |||||
// UserAccount userAccount = ldgDS.getUserAccountSet().register(userKP.getAddress(), userKP.getPubKey()); | |||||
// userAccount.setProperty("Name", "孙悟空", -1); | |||||
// userAccount.setProperty("Age", "10000", -1); | |||||
// System.out.println("UserAddress=" + userAccount.getAddress()); | |||||
// | |||||
// UserAccount gatewayUserAccount = ldgDS.getUserAccountSet().register(gatewayKey.getAddress(), gatewayKey.getPubKey()); | |||||
// userAccount.setProperty("Name", "齐天大圣", -1); | |||||
// userAccount.setProperty("Age", "11111", -1); | |||||
// System.out.println("gatewayUserAccount=" + userAccount.getAddress()); | |||||
// | |||||
// //注册contract; | |||||
//// SignatureDigest signDigest = CryptoUtils.sign(CryptoAlgorithm.ED25519).sign(contractKey.getPrivKey(), Base58Utils.decode(contractKey.getAddress())); | |||||
//// DigitalSignatureBlob dgtSign = new DigitalSignatureBlob(contractKey.getPubKey(), signDigest); | |||||
//// ldgDS.getContractAccountSet().deploy(contractKey.getAddress(),contractKey.getPubKey(),dgtSign,this.contractBytes()); | |||||
// | |||||
// // 提交交易结果; | |||||
// LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS); | |||||
// | |||||
// // 生成区块; | |||||
// LedgerBlock genesisBlock = ldgEdt.prepare(); | |||||
// ldgEdt.commit(); | |||||
// return genesisBlock.getHash(); | |||||
// } | |||||
// | |||||
//// private HashDigest handleContractEvent(LedgerEditor ldgEdt,BlockchainKeyPair userKP, | |||||
//// BlockchainKeyPair gatewayKey,BlockchainKeyPair contractKey) { | |||||
//// // 记录交易,注册用户; | |||||
//// LedgerTransactionContext txCtx = ldgEdt.newTransaction( | |||||
//// this.genContractReq(ledgerHash,sponsorKey,gatewayKey,contractKey)); | |||||
//// LedgerDataSet ldgDS = txCtx.getDataSet(); | |||||
//// | |||||
//// //注册contract; | |||||
//// SignatureDigest signDigest = CryptoUtils.sign(CryptoAlgorithm.ED25519).sign(contractKey.getPrivKey(), Base58Utils.decode(contractKey.getAddress())); | |||||
//// DigitalSignatureBlob dgtSign = new DigitalSignatureBlob(contractKey.getPubKey(), signDigest); | |||||
//// ldgDS.getContractAccountSet().deploy(contractKey.getAddress(),contractKey.getPubKey(),dgtSign,this.getChainCodeBytes()); | |||||
//// | |||||
//// // 提交交易结果; | |||||
//// LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS); | |||||
//// | |||||
//// // 生成区块; | |||||
//// LedgerBlock newBlock = ldgEdt.prepare(); | |||||
//// ldgEdt.commit(); | |||||
//// return newBlock.getHash(); | |||||
//// } | |||||
// | |||||
// private byte[] getChainCodeBytes(){ | |||||
// //构建合约的字节数组; | |||||
// byte[] contractCode= null; | |||||
// File file = null; | |||||
// InputStream input = null; | |||||
// try { | |||||
// String contractZipName = "AssetContract1.contract"; | |||||
// URL url = Paths.get(ContractPath.getOutputPath() + contractZipName).toUri().toURL(); | |||||
// file = new File(url.toURI()); | |||||
// assertTrue("contract zip file is not exist.",file.exists()==true); | |||||
// input = new FileInputStream(file); | |||||
// //这种暴力的读取压缩包,在class解析时有问题,所有需要改进; | |||||
// contractCode = new byte[input.available()]; | |||||
// input.read(contractCode); | |||||
// } catch (URISyntaxException e) { | |||||
// e.printStackTrace(); | |||||
// } catch (IOException e) { | |||||
// e.printStackTrace(); | |||||
// } finally { | |||||
// try { | |||||
// if(input!=null){ | |||||
// input.close(); | |||||
// } | |||||
// } catch (IOException e) { | |||||
// e.printStackTrace(); | |||||
// } | |||||
// } | |||||
// return contractCode; | |||||
// } | |||||
// | |||||
// private LedgerInitSetting createLedgerInitSetting() { | |||||
// CryptoConfig defCryptoSetting = new CryptoConfig(); | |||||
// defCryptoSetting.setAutoVerifyHash(true); | |||||
// defCryptoSetting.setHashAlgorithm(CryptoAlgorithm.SHA256); | |||||
// | |||||
// LedgerInitSettingData initSetting = new LedgerInitSettingData(); | |||||
// | |||||
// initSetting.setLedgerSeed(BytesUtils.toBytes("A Test Ledger seed!", "UTF-8")); | |||||
// initSetting.setCryptoSetting(defCryptoSetting); | |||||
// ConsensusParticipantData[] parties = new ConsensusParticipantData[4]; | |||||
// parties[0] = new ConsensusParticipantData(); | |||||
// parties[0].setId(0); | |||||
// parties[0].setName("John"); | |||||
// CryptoKeyPair kp0 = CryptoUtils.sign(CryptoAlgorithm.ED25519).generateKeyPair(); | |||||
// parties[0].setPubKey(kp0.getPubKey()); | |||||
// parties[0].setHostAddress(new NetworkAddress("127.0.0.1", 9000)); | |||||
// | |||||
// parties[1] = new ConsensusParticipantData(); | |||||
// parties[1].setId(1); | |||||
// parties[1].setName("Mary"); | |||||
// CryptoKeyPair kp1 = CryptoUtils.sign(CryptoAlgorithm.ED25519).generateKeyPair(); | |||||
// parties[1].setPubKey(kp1.getPubKey()); | |||||
// parties[1].setHostAddress(new NetworkAddress("127.0.0.1", 9010)); | |||||
// | |||||
// parties[2] = new ConsensusParticipantData(); | |||||
// parties[2].setId(2); | |||||
// parties[2].setName("Jerry"); | |||||
// CryptoKeyPair kp2 = CryptoUtils.sign(CryptoAlgorithm.ED25519).generateKeyPair(); | |||||
// parties[2].setPubKey(kp1.getPubKey()); | |||||
// parties[2].setHostAddress(new NetworkAddress("127.0.0.1", 9020)); | |||||
// | |||||
// parties[3] = new ConsensusParticipantData(); | |||||
// parties[3].setId(3); | |||||
// parties[3].setName("Tom"); | |||||
// CryptoKeyPair kp3 = CryptoUtils.sign(CryptoAlgorithm.ED25519).generateKeyPair(); | |||||
// parties[3].setPubKey(kp1.getPubKey()); | |||||
// parties[3].setHostAddress(new NetworkAddress("127.0.0.1", 9030)); | |||||
// | |||||
// initSetting.setConsensusParticipants(parties); | |||||
// | |||||
// return initSetting; | |||||
// } | |||||
//} | |||||
package test.com.jd.blockchain.ledger; | |||||
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.DataContractRegistry; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||||
import com.jd.blockchain.ledger.EndpointRequest; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | |||||
import com.jd.blockchain.ledger.LedgerInitSetting; | |||||
import com.jd.blockchain.ledger.LedgerTransaction; | |||||
import com.jd.blockchain.ledger.NodeRequest; | |||||
import com.jd.blockchain.ledger.TransactionContent; | |||||
import com.jd.blockchain.ledger.TransactionContentBody; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | |||||
import com.jd.blockchain.ledger.TransactionResponse; | |||||
import com.jd.blockchain.ledger.TransactionState; | |||||
import com.jd.blockchain.ledger.UserRegisterOperation; | |||||
import com.jd.blockchain.ledger.core.LedgerDataSet; | |||||
import com.jd.blockchain.ledger.core.LedgerEditor; | |||||
import com.jd.blockchain.ledger.core.LedgerRepository; | |||||
import com.jd.blockchain.ledger.core.LedgerTransactionContext; | |||||
import com.jd.blockchain.ledger.core.UserAccount; | |||||
import com.jd.blockchain.ledger.core.impl.DefaultOperationHandleRegisteration; | |||||
import com.jd.blockchain.ledger.core.impl.LedgerManager; | |||||
import com.jd.blockchain.ledger.core.impl.LedgerTransactionalEditor; | |||||
import com.jd.blockchain.ledger.core.impl.OperationHandleRegisteration; | |||||
import com.jd.blockchain.ledger.core.impl.TransactionBatchProcessor; | |||||
import com.jd.blockchain.storage.service.utils.MemoryKVStorage; | |||||
public class TransactionBatchProcessorTest { | |||||
static { | |||||
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(UserRegisterOperation.class); | |||||
} | |||||
private static final String LEDGER_KEY_PREFIX = "LDG://"; | |||||
private HashDigest ledgerHash = null; | |||||
private BlockchainKeypair parti0 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair parti1 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); | |||||
private TransactionRequest transactionRequest; | |||||
// 采用基于内存的 Storage; | |||||
private MemoryKVStorage storage = new MemoryKVStorage(); | |||||
@Test | |||||
public void testTxReqProcess() { | |||||
// 初始化账本到指定的存储库; | |||||
ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3); | |||||
// 加载账本; | |||||
LedgerManager ledgerManager = new LedgerManager(); | |||||
LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage); | |||||
// 验证参与方账户的存在; | |||||
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | |||||
UserAccount user0 = previousBlockDataset.getUserAccountSet().getUser(parti0.getAddress()); | |||||
assertNotNull(user0); | |||||
boolean partiRegistered = previousBlockDataset.getUserAccountSet().contains(parti0.getAddress()); | |||||
assertTrue(partiRegistered); | |||||
// 生成新区块; | |||||
LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); | |||||
OperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration(); | |||||
TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, | |||||
opReg, ledgerManager); | |||||
// 注册新用户; | |||||
BlockchainKeypair userKeypair = BlockchainKeyGenerator.getInstance().generate(); | |||||
transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair, parti0); | |||||
txbatchProcessor.schedule(transactionRequest); | |||||
LedgerBlock newBlock = newBlockEditor.prepare(); | |||||
newBlockEditor.commit(); | |||||
// 验证正确性; | |||||
ledgerManager = new LedgerManager(); | |||||
ledgerRepo = ledgerManager.register(ledgerHash, storage); | |||||
LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | |||||
assertEquals(newBlock.getHash(), latestBlock.getHash()); | |||||
assertEquals(1, newBlock.getHeight()); | |||||
} | |||||
private HashDigest initLedger(MemoryKVStorage storage, BlockchainKeypair... partiKeys) { | |||||
// 创建初始化配置; | |||||
LedgerInitSetting initSetting = LedgerTestUtils.createLedgerInitSetting(partiKeys); | |||||
// 创建账本; | |||||
LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | |||||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||||
LedgerTransactionContext genisisTxCtx = ldgEdt.newTransaction(genesisTxReq); | |||||
LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | |||||
for (int i = 0; i < partiKeys.length; i++) { | |||||
UserAccount userAccount = ldgDS.getUserAccountSet().register(partiKeys[i].getAddress(), | |||||
partiKeys[i].getPubKey()); | |||||
userAccount.setProperty("Name", "参与方-" + i, -1); | |||||
userAccount.setProperty("Share", "" + (10 + i), -1); | |||||
} | |||||
LedgerTransaction tx = genisisTxCtx.commit(TransactionState.SUCCESS); | |||||
assertEquals(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); | |||||
assertEquals(0, tx.getBlockHeight()); | |||||
LedgerBlock block = ldgEdt.prepare(); | |||||
assertEquals(0, block.getHeight()); | |||||
assertNotNull(block.getHash()); | |||||
assertNull(block.getPreviousHash()); | |||||
assertEquals(block.getHash(), block.getLedgerHash()); | |||||
// 提交数据,写入存储; | |||||
ldgEdt.commit(); | |||||
HashDigest ledgerHash = block.getHash(); | |||||
return ledgerHash; | |||||
} | |||||
} |
@@ -1,5 +1,6 @@ | |||||
package test.com.jd.blockchain.ledger; | package test.com.jd.blockchain.ledger; | ||||
import static org.junit.Assert.assertArrayEquals; | |||||
import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
import static org.junit.Assert.assertFalse; | import static org.junit.Assert.assertFalse; | ||||
import static org.junit.Assert.assertNotNull; | import static org.junit.Assert.assertNotNull; | ||||
@@ -10,10 +11,13 @@ import java.util.Random; | |||||
import org.junit.Test; | import org.junit.Test; | ||||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
import com.jd.blockchain.binaryproto.DataContractRegistry; | import com.jd.blockchain.binaryproto.DataContractRegistry; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | import com.jd.blockchain.ledger.BlockchainKeyGenerator; | ||||
import com.jd.blockchain.ledger.BlockchainKeypair; | import com.jd.blockchain.ledger.BlockchainKeypair; | ||||
import com.jd.blockchain.ledger.BytesDataList; | |||||
import com.jd.blockchain.ledger.BytesValueList; | |||||
import com.jd.blockchain.ledger.ContractCodeDeployOperation; | import com.jd.blockchain.ledger.ContractCodeDeployOperation; | ||||
import com.jd.blockchain.ledger.ContractEventSendOperation; | import com.jd.blockchain.ledger.ContractEventSendOperation; | ||||
import com.jd.blockchain.ledger.CryptoSetting; | import com.jd.blockchain.ledger.CryptoSetting; | ||||
@@ -67,8 +71,8 @@ public class TransactionSetTest { | |||||
BlockchainKeypair dataKey = BlockchainKeyGenerator.getInstance().generate(); | BlockchainKeypair dataKey = BlockchainKeyGenerator.getInstance().generate(); | ||||
DataAccountRegisterOperation dataAccRegOp = txBuilder.dataAccounts().register(dataKey.getIdentity()); | DataAccountRegisterOperation dataAccRegOp = txBuilder.dataAccounts().register(dataKey.getIdentity()); | ||||
DataAccountKVSetOperation kvsetOP = txBuilder.dataAccount(dataKey.getAddress()) | |||||
.setText("A", "Value_A_0", -1).setText("B", "Value_B_0", -1).getOperation(); | |||||
DataAccountKVSetOperation kvsetOP = txBuilder.dataAccount(dataKey.getAddress()).setText("A", "Value_A_0", -1) | |||||
.setText("B", "Value_B_0", -1).getOperation(); | |||||
byte[] chainCode = new byte[128]; | byte[] chainCode = new byte[128]; | ||||
rand.nextBytes(chainCode); | rand.nextBytes(chainCode); | ||||
@@ -76,7 +80,7 @@ public class TransactionSetTest { | |||||
ContractCodeDeployOperation contractDplOP = txBuilder.contracts().deploy(contractKey.getIdentity(), chainCode); | ContractCodeDeployOperation contractDplOP = txBuilder.contracts().deploy(contractKey.getIdentity(), chainCode); | ||||
ContractEventSendOperation contractEvtSendOP = txBuilder.contractEvents().send(contractKey.getAddress(), "test", | ContractEventSendOperation contractEvtSendOP = txBuilder.contractEvents().send(contractKey.getAddress(), "test", | ||||
"TestContractArgs".getBytes()); | |||||
BytesDataList.singleText("TestContractArgs")); | |||||
TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | ||||
@@ -98,7 +102,8 @@ public class TransactionSetTest { | |||||
txSnapshot.setContractAccountSetHash(contractAccountSetHash); | txSnapshot.setContractAccountSetHash(contractAccountSetHash); | ||||
long blockHeight = 8922L; | long blockHeight = 8922L; | ||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txReq, TransactionState.SUCCESS, txSnapshot); | |||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txReq, TransactionState.SUCCESS, txSnapshot, | |||||
null); | |||||
txset.add(tx); | txset.add(tx); | ||||
assertTrue(txset.isUpdated()); | assertTrue(txset.isUpdated()); | ||||
@@ -172,7 +177,8 @@ public class TransactionSetTest { | |||||
for (int i = 0; i < acutualKVWriteSet.length; i++) { | for (int i = 0; i < acutualKVWriteSet.length; i++) { | ||||
assertEquals(expKVWriteSet[i].getKey(), acutualKVWriteSet[i].getKey()); | assertEquals(expKVWriteSet[i].getKey(), acutualKVWriteSet[i].getKey()); | ||||
assertEquals(expKVWriteSet[i].getExpectedVersion(), acutualKVWriteSet[i].getExpectedVersion()); | assertEquals(expKVWriteSet[i].getExpectedVersion(), acutualKVWriteSet[i].getExpectedVersion()); | ||||
assertTrue(BytesUtils.equals(expKVWriteSet[i].getValue().getValue().toBytes(), acutualKVWriteSet[i].getValue().getValue().toBytes())); | |||||
assertTrue(BytesUtils.equals(expKVWriteSet[i].getValue().getValue().toBytes(), | |||||
acutualKVWriteSet[i].getValue().getValue().toBytes())); | |||||
} | } | ||||
ContractCodeDeployOperation actualContractDplOp = (ContractCodeDeployOperation) actualOperations[3]; | ContractCodeDeployOperation actualContractDplOp = (ContractCodeDeployOperation) actualOperations[3]; | ||||
@@ -184,8 +190,14 @@ public class TransactionSetTest { | |||||
assertEquals(contractEvtSendOP.getContractAddress(), actualContractEvtSendOp.getContractAddress()); | assertEquals(contractEvtSendOP.getContractAddress(), actualContractEvtSendOp.getContractAddress()); | ||||
assertEquals(contractEvtSendOP.getEvent(), actualContractEvtSendOp.getEvent()); | assertEquals(contractEvtSendOP.getEvent(), actualContractEvtSendOp.getEvent()); | ||||
assertEquals("test", actualContractEvtSendOp.getEvent()); | assertEquals("test", actualContractEvtSendOp.getEvent()); | ||||
assertTrue(BytesUtils.equals(contractEvtSendOP.getArgs(), actualContractEvtSendOp.getArgs())); | |||||
assertTrue(BytesUtils.equals("TestContractArgs".getBytes(), actualContractEvtSendOp.getArgs())); | |||||
byte[] expectedBytes = BinaryProtocol.encode(contractEvtSendOP.getArgs(), BytesValueList.class); | |||||
byte[] actualBytes = BinaryProtocol.encode(actualContractEvtSendOp.getArgs(), BytesValueList.class); | |||||
assertArrayEquals(expectedBytes, actualBytes); | |||||
expectedBytes = BinaryProtocol.encode(BytesDataList.singleText("TestContractArgs"), BytesValueList.class); | |||||
actualBytes = BinaryProtocol.encode(actualContractEvtSendOp.getArgs(), BytesValueList.class); | |||||
assertArrayEquals(expectedBytes, actualBytes); | |||||
} | } | ||||
} | } |
@@ -39,7 +39,6 @@ | |||||
<version>${project.version}</version> | <version>${project.version}</version> | ||||
<scope>test</scope> | <scope>test</scope> | ||||
</dependency> | </dependency> | ||||
</dependencies> | </dependencies> | ||||
</project> | </project> |
@@ -0,0 +1,11 @@ | |||||
package com.jd.blockchain.contract; | |||||
/** | |||||
* 合约实现 {@link ContractAware} 的子接口可以监听运行时的生命周期事件; | |||||
* | |||||
* @author huanghaiquan | |||||
* | |||||
*/ | |||||
public interface ContractAware { | |||||
} |
@@ -1,11 +1,12 @@ | |||||
package com.jd.blockchain.contract; | package com.jd.blockchain.contract; | ||||
import java.util.Set; | |||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.BlockchainIdentity; | import com.jd.blockchain.ledger.BlockchainIdentity; | ||||
import com.jd.blockchain.ledger.BytesValueList; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
import java.util.Set; | |||||
public interface ContractEventContext { | public interface ContractEventContext { | ||||
@@ -42,7 +43,7 @@ public interface ContractEventContext { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
byte[] getArgs(); | |||||
BytesValueList getArgs(); | |||||
/** | /** | ||||
* 账本操作上下文; | * 账本操作上下文; | ||||
@@ -1,16 +1,17 @@ | |||||
package com.jd.blockchain.contract; | package com.jd.blockchain.contract; | ||||
public class ContractException extends RuntimeException { | |||||
import com.jd.blockchain.ledger.LedgerException; | |||||
public class ContractException extends LedgerException { | |||||
private static final long serialVersionUID = -4643365435848655115L; | |||||
public ContractException(String message) { | public ContractException(String message) { | ||||
super(message); | super(message); | ||||
} | } | ||||
public ContractException(String message,ErrorCodeEnum errorCodeEnum) { | |||||
super(message+","+errorCodeEnum.toString()); | |||||
public ContractException(String message, Throwable cause) { | |||||
super(message, cause); | |||||
} | } | ||||
public ContractException(ErrorCodeEnum errorCodeEnum) { | |||||
super(errorCodeEnum.toString()); | |||||
} | |||||
} | } |
@@ -7,7 +7,7 @@ package com.jd.blockchain.contract; | |||||
* @author huanghaiquan | * @author huanghaiquan | ||||
* | * | ||||
*/ | */ | ||||
public interface ContractAppLifecycleAwire extends ContractRuntimeAwire { | |||||
public interface ContractLifecycleAware extends ContractAware { | |||||
void postConstruct(); | void postConstruct(); | ||||
@@ -1,11 +0,0 @@ | |||||
package com.jd.blockchain.contract; | |||||
/** | |||||
* 合约实现 {@link ContractRuntimeAwire} 的子接口可以监听运行时的生命周期事件; | |||||
* | |||||
* @author huanghaiquan | |||||
* | |||||
*/ | |||||
public interface ContractRuntimeAwire { | |||||
} |
@@ -1,196 +1,165 @@ | |||||
package com.jd.blockchain.contract; | |||||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
import com.jd.blockchain.ledger.*; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.IllegalDataException; | |||||
import org.springframework.util.ReflectionUtils; | |||||
import java.nio.ByteBuffer; | |||||
import java.util.Arrays; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
/** | |||||
* @author zhaogw | |||||
* date 2019/5/16 18:05 | |||||
*/ | |||||
public class ContractSerializeUtils { | |||||
public static Map<Integer, Class<?>> DATA_CONTRACT_MAP = new HashMap<>(); | |||||
public static final Integer[] PRIMITIVE_DATA_CODES = {DataCodes.CONTRACT_INT8, DataCodes.CONTRACT_INT16, DataCodes.CONTRACT_INT32, | |||||
DataCodes.CONTRACT_INT64, DataCodes.CONTRACT_BIG_INT,DataCodes.CONTRACT_TEXT, DataCodes.CONTRACT_BINARY }; | |||||
/** | |||||
* serialize the Object[] by List<DataContract> list; | |||||
* @param objArr | |||||
* @param dataContractList | |||||
* @return | |||||
*/ | |||||
public static byte[] serializeMethodParam(Object[] objArr,List<DataContract> dataContractList) { | |||||
byte[][] result = new byte[objArr.length][]; | |||||
//将method中形参转换为实体对象,每个形参都必须为@DataContract类型;每个形参使用系统的BinaryProtocol来进行序列化,如果有5个参数,则使用5次序列化; | |||||
int sum = 0; | |||||
for(int i=0;i<objArr.length;i++){ | |||||
DataContract dataContract = dataContractList.get(i); | |||||
objArr[i] = regenObj(dataContract,objArr[i]); | |||||
//get data interface; | |||||
result[i] = BinaryProtocol.encode(objArr[i],getDataIntf().get(dataContract.code())); | |||||
sum += result[i].length; | |||||
} | |||||
/** | |||||
* return byte[] format: | |||||
return is byte[], but now is byte[][], so we should reduct dimension by adding the header info to the rtnBytes[]; | |||||
rtnBytes[]=classTypes.length/first length/second length/third length/result[0]/result[1]/result[2]; | |||||
rtnBytes[0]: 4 bytes(classTypes.length); | |||||
rtnBytes[1]: 4 bytes(1 param's length); | |||||
rtnBytes[2]: 4 bytes(2 param's length); | |||||
rtnBytes[3]: 4 bytes(3 param's length); | |||||
rtnBytes[...]: result[0][] bytes(1 param's length); | |||||
rtnBytes[...]: result[1][] bytes(2 param's length); | |||||
rtnBytes[...]: result[2][] bytes(3 param's length); | |||||
*/ | |||||
int bodyFirstPosition = 4 + 4 * (objArr.length); | |||||
ByteBuffer byteBuffer = ByteBuffer.allocate(bodyFirstPosition + sum); | |||||
byteBuffer.putInt(objArr.length); | |||||
for(int j=0; j<result.length; j++) { | |||||
byte[] curResult = result[j]; | |||||
byteBuffer.putInt(curResult.length); | |||||
} | |||||
for(int k=0; k<result.length; k++){ | |||||
byteBuffer.put(result[k]); | |||||
} | |||||
return byteBuffer.array(); | |||||
} | |||||
/** | |||||
* deserialize the params bytes[]; | |||||
* params format: nums|first length| second length| third length| ... |bytes[0]| byte[1] | bytes[2]| ... | |||||
* @param params | |||||
* @param dataContractList | |||||
* @return | |||||
*/ | |||||
public static Object[] deserializeMethodParam(byte[] params, List<DataContract> dataContractList) { | |||||
Object result[] = new Object[dataContractList.size()]; | |||||
ByteBuffer byteBuffer = ByteBuffer.allocate(params.length); | |||||
byteBuffer.put(params); | |||||
int paramNums = byteBuffer.getInt(0); | |||||
if(paramNums != dataContractList.size()){ | |||||
throw new IllegalArgumentException("deserialize Method param. params'length in byte[] != method's param length"); | |||||
} | |||||
int offsetPosition = (1 + dataContractList.size())*4; //start position of real data; | |||||
for(int i=0; i<dataContractList.size(); i++){ | |||||
DataContract dataContract = dataContractList.get(i); | |||||
int curParamLength = byteBuffer.getInt((i+1)*4); | |||||
ByteBuffer byteBuffer1 = ByteBuffer.allocate(curParamLength); | |||||
byteBuffer1.put(params,offsetPosition,curParamLength); | |||||
offsetPosition += curParamLength; | |||||
//if dataContract=primitive type(byte/short/int/long/String),only use its getValues(); | |||||
Object object = BinaryProtocol.decodeAs(byteBuffer1.array(), | |||||
getDataIntf().get(dataContract.code())); | |||||
if(isPrimitiveType(dataContract.code())){ | |||||
Class<?> classObj = getDataIntf().get(dataContract.code()); | |||||
try { | |||||
result[i] = ReflectionUtils.invokeMethod(classObj.getMethod("getValue"),object); | |||||
} catch (NoSuchMethodException e) { | |||||
throw new IllegalStateException("no getValue(). detail="+e.getMessage()); | |||||
} | |||||
}else { | |||||
result[i] = object; | |||||
} | |||||
byteBuffer1.clear(); | |||||
} | |||||
return result; | |||||
} | |||||
/** | |||||
* the param types that we can support; | |||||
* @param <T> | |||||
* @return | |||||
*/ | |||||
public static <T> Map<Integer, Class<?> > getDataIntf(){ | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT8, CONTRACT_INT8.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT16, CONTRACT_INT16.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT32, CONTRACT_INT32.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT64, CONTRACT_INT64.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_TEXT, CONTRACT_TEXT.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BINARY, CONTRACT_BINARY.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BIG_INT, CONTRACT_BIG_INT.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BIZ_CONTENT, ContractBizContent.class); | |||||
return DATA_CONTRACT_MAP; | |||||
} | |||||
public static boolean isPrimitiveType(int dataContractCode){ | |||||
return Arrays.asList(PRIMITIVE_DATA_CODES).contains(dataContractCode); | |||||
} | |||||
private static Object regenObj(DataContract dataContract, Object object){ | |||||
if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT8.class)){ | |||||
return (CONTRACT_INT8) () -> Byte.parseByte(object.toString()); | |||||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT16.class)){ | |||||
return (CONTRACT_INT16) () -> Short.parseShort(object.toString()); | |||||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT32.class)){ | |||||
return (CONTRACT_INT32) () -> Integer.parseInt(object.toString()); | |||||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT64.class)){ | |||||
return (CONTRACT_INT64) () -> Long.parseLong(object.toString()); | |||||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_TEXT.class)){ | |||||
return (CONTRACT_TEXT) () -> object.toString(); | |||||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_BINARY.class)){ | |||||
return (CONTRACT_BINARY) () -> (Bytes) object; | |||||
}else if(getDataIntf().get(dataContract.code()).equals(ContractBizContent.class)){ | |||||
ContractBizContent contractBizContent = (ContractBizContent)object; | |||||
return contractBizContent; | |||||
}else { | |||||
throw new IllegalDataException("cann't get new Object by dataContract and object."); | |||||
} | |||||
} | |||||
/** | |||||
* get contractType(contain @DataContract) by primitive class type; | |||||
* some class type can be supported default (byte/char/int/long/String/Bytes, and so on). | |||||
* in other words, need not contain the @DataContract in its class for contract param's serialization or deserialization. | |||||
* @param classType | |||||
* @return | |||||
*/ | |||||
private static Class<?> getContractTypeByPrimitiveType(Class<?> classType) { | |||||
if(classType.equals(byte.class) || classType.equals(Byte.class)){ | |||||
return CONTRACT_INT8.class; | |||||
}else if(classType.equals(char.class) || classType.equals(Character.class)){ | |||||
return CONTRACT_INT16.class; | |||||
}else if(classType.equals(int.class) || classType.equals(Integer.class)){ | |||||
return CONTRACT_INT32.class; | |||||
}else if(classType.equals(long.class) || classType.equals(Long.class)){ | |||||
return CONTRACT_INT64.class; | |||||
}else if(classType.equals(String.class)){ | |||||
return CONTRACT_TEXT.class; | |||||
}else if(classType.equals(Bytes.class)){ | |||||
return CONTRACT_BINARY.class; | |||||
}else { | |||||
throw new IllegalDataException(String.format("no support the classType=%s, please check @DataContract.",classType.toString())); | |||||
} | |||||
} | |||||
public static DataContract parseDataContract(Class<?> classType){ | |||||
DataContract dataContract = classType.getAnnotation(DataContract.class); | |||||
//if the param's class Type don't contain @DataContract, then check parameterAnnotations of this method. | |||||
if(dataContract == null){ | |||||
boolean canPass = false; | |||||
//if parameterAnnotations don't contain @DataContract, is it primitive type? | |||||
Class<?> contractType = getContractTypeByPrimitiveType(classType); | |||||
dataContract = contractType.getAnnotation(DataContract.class); | |||||
} | |||||
if(!getDataIntf().containsKey(dataContract.code())){ | |||||
throw new IllegalArgumentException(String.format( | |||||
"for now, this @dataContract(code=%s) is forbidden in the param list.",dataContract.code())); | |||||
} | |||||
return dataContract; | |||||
} | |||||
} | |||||
//package com.jd.blockchain.contract; | |||||
// | |||||
//import java.util.ArrayList; | |||||
//import java.util.HashMap; | |||||
//import java.util.List; | |||||
//import java.util.Map; | |||||
// | |||||
//import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
//import com.jd.blockchain.binaryproto.DataContract; | |||||
//import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
//import com.jd.blockchain.contract.param.WRAP_BYTES; | |||||
//import com.jd.blockchain.contract.param.WRAP_INT; | |||||
//import com.jd.blockchain.contract.param.WRAP_LONG; | |||||
//import com.jd.blockchain.contract.param.WRAP_SHORT; | |||||
//import com.jd.blockchain.contract.param.WRAP_STRING; | |||||
//import com.jd.blockchain.ledger.BytesValue; | |||||
//import com.jd.blockchain.ledger.BytesValueList; | |||||
//import com.jd.blockchain.utils.io.BytesUtils; | |||||
// | |||||
//public class ContractSerializeUtils { | |||||
// | |||||
// final static int INT_LENGTH = 4; | |||||
// | |||||
// static Map<Class<?>, Class<?>> MAP = new HashMap<>(); | |||||
// | |||||
// static { | |||||
// MAP.put(byte[].class, WRAP_BYTES.class); | |||||
// MAP.put(Short.class, WRAP_SHORT.class); | |||||
// MAP.put(short.class, WRAP_SHORT.class); | |||||
// MAP.put(Integer.class, WRAP_INT.class); | |||||
// MAP.put(int.class, WRAP_INT.class); | |||||
// MAP.put(Long.class, WRAP_LONG.class); | |||||
// MAP.put(long.class, WRAP_LONG.class); | |||||
// MAP.put(String.class, WRAP_STRING.class); | |||||
// | |||||
// DataContractRegistry.register(WRAP_BYTES.class); | |||||
// DataContractRegistry.register(WRAP_SHORT.class); | |||||
// DataContractRegistry.register(WRAP_INT.class); | |||||
// DataContractRegistry.register(WRAP_LONG.class); | |||||
// DataContractRegistry.register(WRAP_STRING.class); | |||||
// } | |||||
// | |||||
// public static boolean support(Class<?> clazz) { | |||||
// if (clazz.isAnnotationPresent(DataContract.class) || MAP.containsKey(clazz)) { | |||||
// return true; | |||||
// } | |||||
// return false; | |||||
// } | |||||
// | |||||
// public static BytesValue serialize(Object data) { | |||||
// | |||||
// if (data == null) { | |||||
// return null; | |||||
// } | |||||
// | |||||
// Class<?> clazz = data.getClass(); | |||||
// Class<?> serialClass; | |||||
// Object wrapData = data; | |||||
// if (clazz.isAnnotationPresent(DataContract.class)) { | |||||
// serialClass = clazz; | |||||
// } else { | |||||
// // 判断类型是否可以序列化 | |||||
// Class<?> wrapClass = MAP.get(clazz); | |||||
// if (wrapClass == null) { | |||||
// throw new IllegalArgumentException("There are Un-Support Type !!!"); | |||||
// } | |||||
// serialClass = wrapClass; | |||||
// | |||||
// if (wrapClass.equals(WRAP_BYTES.class)) { | |||||
// wrapData = (WRAP_BYTES) () -> (byte[]) data; | |||||
// } else if (wrapClass.equals(WRAP_INT.class)) { | |||||
// wrapData = (WRAP_INT) () -> (int) data; | |||||
// } else if (wrapClass.equals(WRAP_LONG.class)) { | |||||
// wrapData = (WRAP_LONG) () -> (long) data; | |||||
// } else if (wrapClass.equals(WRAP_SHORT.class)) { | |||||
// wrapData = (WRAP_SHORT) () -> (short) data; | |||||
// } else if (wrapClass.equals(WRAP_STRING.class)) { | |||||
// wrapData = (WRAP_STRING) () -> (String) data; | |||||
// } | |||||
// } | |||||
// // 按照对应接口进行序列化 | |||||
// // 生成该接口对应的对象 | |||||
// return BinaryProtocol.encode(wrapData, serialClass); | |||||
// } | |||||
// | |||||
// public static BytesValueList serializeArray(Object[] datas) { | |||||
// if (datas == null || datas.length == 0) { | |||||
// return null; | |||||
// } | |||||
// int contentBytesSize = 0; | |||||
// byte[] header = new byte[(datas.length + 1) * 4]; | |||||
// System.arraycopy(BytesUtils.toBytes(datas.length), 0, header, 0, INT_LENGTH); | |||||
// int offset = INT_LENGTH; | |||||
// | |||||
// List<byte[]> serialBytesList = new ArrayList<>(); | |||||
// for (Object data : datas) { | |||||
// // 按照对应接口进行序列化 | |||||
// byte[] currBytes = serialize(data); | |||||
// // 长度写入 | |||||
// System.arraycopy(BytesUtils.toBytes(currBytes.length), 0, header, offset, INT_LENGTH); | |||||
// serialBytesList.add(currBytes); | |||||
// contentBytesSize += currBytes.length; | |||||
// offset += INT_LENGTH; | |||||
// } | |||||
// | |||||
// // 填充content | |||||
// byte[] content = new byte[contentBytesSize]; | |||||
// offset = 0; | |||||
// for (byte[] bytes : serialBytesList) { | |||||
// System.arraycopy(bytes, 0, content, offset, bytes.length); | |||||
// offset += bytes.length; | |||||
// } | |||||
// // 将header和content组装 | |||||
// return BytesUtils.concat(header, content); | |||||
// } | |||||
// | |||||
// public static Object[] resolveArray(byte[] bytes) { | |||||
// if (bytes == null || bytes.length == 0) { | |||||
// return null; | |||||
// } | |||||
// byte[] lengthBytes = new byte[INT_LENGTH]; | |||||
// System.arraycopy(bytes, 0, lengthBytes, 0, INT_LENGTH); | |||||
// int length = BytesUtils.toInt(lengthBytes); | |||||
// Object[] datas = new Object[length]; | |||||
// | |||||
// int headerOffset = INT_LENGTH; | |||||
// int contentStart = (length + 1) * INT_LENGTH; | |||||
// | |||||
// for (int i = 0; i < length; i++) { | |||||
// byte[] currDataLengthBytes = new byte[INT_LENGTH]; | |||||
// System.arraycopy(bytes, headerOffset, currDataLengthBytes, 0, INT_LENGTH); | |||||
// int currDataLength = BytesUtils.toInt(currDataLengthBytes); | |||||
// // 读取 | |||||
// byte[] dataBytes = new byte[currDataLength]; | |||||
// System.arraycopy(bytes, contentStart, dataBytes, 0, currDataLength); | |||||
// | |||||
// datas[i] = resolve(dataBytes); | |||||
// | |||||
// contentStart += currDataLength; | |||||
// headerOffset += INT_LENGTH; | |||||
// } | |||||
// | |||||
// return datas; | |||||
// } | |||||
// | |||||
// public static Object resolve(BytesValue bytes) { | |||||
// // 反序列化该接口 | |||||
// Object currData = BinaryProtocol.decode(bytes); | |||||
// | |||||
// // 代理对象类型不能使用class判断,只能使用instanceof | |||||
// if (currData instanceof WRAP_STRING) { | |||||
// return ((WRAP_STRING) currData).getValue(); | |||||
// } else if (currData instanceof WRAP_INT) { | |||||
// return ((WRAP_INT) currData).getValue(); | |||||
// } else if (currData instanceof WRAP_LONG) { | |||||
// return ((WRAP_LONG) currData).getValue(); | |||||
// } else if (currData instanceof WRAP_BYTES) { | |||||
// return ((WRAP_BYTES) currData).getValue(); | |||||
// } else if (currData instanceof WRAP_SHORT) { | |||||
// return ((WRAP_SHORT) currData).getValue(); | |||||
// } else { | |||||
// return currData; | |||||
// } | |||||
// } | |||||
//} |
@@ -0,0 +1,147 @@ | |||||
package com.jd.blockchain.contract; | |||||
import java.lang.reflect.Method; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import java.util.Set; | |||||
import com.jd.blockchain.ledger.BytesValueEncoding; | |||||
import com.jd.blockchain.utils.IllegalDataException; | |||||
public class ContractType { | |||||
private String name; | |||||
private Map<String, Method> events = new HashMap<>(); | |||||
private Map<Method, String> handleMethods = new HashMap<>(); | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
/** | |||||
* 返回声明的所有事件; | |||||
* | |||||
* @return | |||||
*/ | |||||
public Set<String> getEvents() { | |||||
return events.keySet(); | |||||
} | |||||
/** | |||||
* 返回指定方法声明的事件;<br> | |||||
* | |||||
* 如果不存在,则返回 null; | |||||
* | |||||
* @param method | |||||
* @return | |||||
*/ | |||||
public String getEvent(Method method) { | |||||
return handleMethods.get(method); | |||||
} | |||||
/** | |||||
* 返回事件的处理方法;<br> | |||||
* | |||||
* 如果不存在,则返回 null; | |||||
* | |||||
* @param event | |||||
* @return | |||||
*/ | |||||
public Method getHandleMethod(String event) { | |||||
return events.get(event); | |||||
} | |||||
private ContractType() { | |||||
} | |||||
/** | |||||
* 解析合约的声明; | |||||
* | |||||
* @param contractIntf 合约的声明接口,必须是 interface ; | |||||
* @return | |||||
*/ | |||||
public static ContractType resolve(Class<?> contractIntf) { | |||||
// TODO:方法会检查合约方法声明的类型和返回值类型; | |||||
// 如果是Class则首先获取其接口 | |||||
if (!contractIntf.isInterface()) { | |||||
Class<?> realIntf = null; | |||||
Class<?>[] interfaces = contractIntf.getInterfaces(); | |||||
for (Class<?> intf : interfaces) { | |||||
if (intf.isAnnotationPresent(Contract.class)) { | |||||
realIntf = intf; | |||||
break; | |||||
} | |||||
} | |||||
if (realIntf == null) { | |||||
throw new IllegalDataException(String | |||||
.format("%s is not a Contract Type, because there is not @Contract !", contractIntf.getName())); | |||||
} | |||||
contractIntf = realIntf; | |||||
} | |||||
// 接口上必须有注解 | |||||
Contract contract = contractIntf.getAnnotation(Contract.class); | |||||
if (contract == null) { | |||||
throw new IllegalDataException("It is not a Contract Type, because there is not @Contract !"); | |||||
} | |||||
Method[] classMethods = contractIntf.getDeclaredMethods(); | |||||
if (classMethods.length == 0) { | |||||
throw new IllegalDataException("This interface have not any methods !"); | |||||
} | |||||
ContractType contractType = new ContractType(); | |||||
// 设置合约显示名字为 | |||||
contractType.name = contract.name(); | |||||
for (Method method : classMethods) { | |||||
// if current method contains @ContractEvent,then put it in this map; | |||||
ContractEvent contractEvent = method.getAnnotation(ContractEvent.class); | |||||
if (contractEvent != null) { | |||||
String eventName = contractEvent.name(); | |||||
// if annoMethodMap has contained the eventName, too many same eventNames exists | |||||
// probably, say NO! | |||||
if (contractType.events.containsKey(eventName)) { | |||||
throw new ContractException("there is repeat definition of contractEvent to @ContractEvent."); | |||||
} | |||||
// check param's type is fit for need. | |||||
Class<?>[] paramTypes = method.getParameterTypes(); | |||||
for (Class<?> currParamType : paramTypes) { | |||||
if (!BytesValueEncoding.supportType(currParamType)) { | |||||
throw new IllegalStateException( | |||||
String.format("Param Type = %s can not support !!!", currParamType.getName())); | |||||
} | |||||
} | |||||
// 判断返回值是否可序列化 | |||||
Class<?> returnType = method.getReturnType(); | |||||
if (!BytesValueEncoding.supportType(returnType)) { | |||||
throw new IllegalStateException( | |||||
String.format("Return Type = %s can not support !!!", returnType.getName())); | |||||
} | |||||
contractType.events.put(eventName, method); | |||||
contractType.handleMethods.put(method, eventName); | |||||
} | |||||
} | |||||
// 最起码有一个ContractEvent | |||||
if (contractType.events.isEmpty()) { | |||||
throw new IllegalStateException( | |||||
String.format("Contract Interface[%s] have none method for annotation[@ContractEvent] !", contractIntf.getName())); | |||||
} | |||||
return contractType; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "ContractType{" + "name='" + name + '\'' + ", events=" + events + ", handleMethods=" + handleMethods | |||||
+ '}'; | |||||
} | |||||
} |
@@ -1,52 +0,0 @@ | |||||
package com.jd.blockchain.contract; | |||||
/** | |||||
* 给每个错误编码,编译快速定位; | |||||
* @Author zhaogw | |||||
* @Date 2018/11/8 15:32 | |||||
*/ | |||||
public enum ErrorCodeEnum { | |||||
//<100为致命错误; | |||||
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!"), | |||||
//其它错误从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; | |||||
} | |||||
} | |||||
/** | |||||
* 给错误分个类,便于汇总; | |||||
*/ | |||||
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; | |||||
} | |||||
@@ -4,7 +4,7 @@ package com.jd.blockchain.contract; | |||||
* @author huanghaiquan | * @author huanghaiquan | ||||
* | * | ||||
*/ | */ | ||||
public interface EventProcessingAwire extends ContractRuntimeAwire { | |||||
public interface EventProcessingAware extends ContractAware { | |||||
/** | /** | ||||
* 在事件处理方法执行之前调用; | * 在事件处理方法执行之前调用; | ||||
@@ -21,19 +21,6 @@ public interface EventProcessingAwire extends ContractRuntimeAwire { | |||||
* @param error | * @param error | ||||
* 错误;如果事件处理正常结束,则此参数为 null;如果事件处理发生了错误,此参数非空; | * 错误;如果事件处理正常结束,则此参数为 null;如果事件处理发生了错误,此参数非空; | ||||
*/ | */ | ||||
void postEvent(ContractEventContext eventContext, ContractException error); | |||||
void postEvent(ContractEventContext eventContext, Exception error); | |||||
/** | |||||
* 在事件处理方法成功执行之后调用; | |||||
* | |||||
* @param error | |||||
* 错误;如果事件处理正常结束,则此参数为 null;如果事件处理发生了错误,此参数非空; | |||||
*/ | |||||
void postEvent(ContractException error); | |||||
/** | |||||
* 在事件处理方法成功执行之后调用; | |||||
*/ | |||||
void postEvent(); | |||||
} | } |
@@ -0,0 +1,39 @@ | |||||
package com.jd.blockchain.contract; | |||||
import com.jd.blockchain.utils.IllegalDataException; | |||||
import java.util.concurrent.CompletableFuture; | |||||
import java.util.concurrent.TimeUnit; | |||||
public class EventResult<T> { | |||||
private static final long MAX_SECONDS = 30; | |||||
private CompletableFuture<T> data = new CompletableFuture<>(); | |||||
private int opIndex; | |||||
public EventResult() { | |||||
} | |||||
public EventResult(int opIndex) { | |||||
this.opIndex = opIndex; | |||||
} | |||||
public void done(T value) { | |||||
data.complete(value); | |||||
} | |||||
public int opIndex() { | |||||
return this.opIndex; | |||||
} | |||||
public T get() { | |||||
try { | |||||
// 防止长时间阻塞 | |||||
return data.get(MAX_SECONDS, TimeUnit.SECONDS); | |||||
} catch (Exception e) { | |||||
throw new IllegalDataException(e.getMessage()); | |||||
} | |||||
} | |||||
} |
@@ -4,6 +4,7 @@ import java.util.Set; | |||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.BlockchainIdentity; | import com.jd.blockchain.ledger.BlockchainIdentity; | ||||
import com.jd.blockchain.ledger.BytesValueList; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
/** | /** | ||||
@@ -13,17 +14,14 @@ import com.jd.blockchain.ledger.TransactionRequest; | |||||
public class LocalContractEventContext implements ContractEventContext,Cloneable { | public class LocalContractEventContext implements ContractEventContext,Cloneable { | ||||
private HashDigest ledgeHash; | private HashDigest ledgeHash; | ||||
private String event; | private String event; | ||||
private byte[] chainCode; | |||||
private byte[] args; | |||||
private BytesValueList args; | |||||
private TransactionRequest transactionRequest; | private TransactionRequest transactionRequest; | ||||
private Set<BlockchainIdentity> txSigners; | private Set<BlockchainIdentity> txSigners; | ||||
private Set<BlockchainIdentity> contractOwners; | |||||
private LedgerContext ledgerContext; | private LedgerContext ledgerContext; | ||||
public LocalContractEventContext(HashDigest ledgeHash, String event){ | public LocalContractEventContext(HashDigest ledgeHash, String event){ | ||||
this.ledgeHash = ledgeHash; | this.ledgeHash = ledgeHash; | ||||
this.event = event; | this.event = event; | ||||
// this.chainCode = chainCode; | |||||
} | } | ||||
@Override | @Override | ||||
@@ -47,7 +45,7 @@ public class LocalContractEventContext implements ContractEventContext,Cloneable | |||||
} | } | ||||
@Override | @Override | ||||
public byte[] getArgs() { | |||||
public BytesValueList getArgs() { | |||||
return args; | return args; | ||||
} | } | ||||
@@ -81,11 +79,6 @@ public class LocalContractEventContext implements ContractEventContext,Cloneable | |||||
return this; | return this; | ||||
} | } | ||||
public LocalContractEventContext setContractOwners(Set<BlockchainIdentity> contractOwners) { | |||||
this.contractOwners = contractOwners; | |||||
return this; | |||||
} | |||||
public LocalContractEventContext setLedgerContext(LedgerContext ledgerContext) { | public LocalContractEventContext setLedgerContext(LedgerContext ledgerContext) { | ||||
this.ledgerContext = ledgerContext; | this.ledgerContext = ledgerContext; | ||||
return this; | return this; | ||||
@@ -100,7 +93,7 @@ public class LocalContractEventContext implements ContractEventContext,Cloneable | |||||
// return this; | // return this; | ||||
// } | // } | ||||
public LocalContractEventContext setArgs(byte[] args) { | |||||
public LocalContractEventContext setArgs(BytesValueList args) { | |||||
this.args = args; | this.args = args; | ||||
return this; | return this; | ||||
} | } | ||||
@@ -8,38 +8,38 @@ import com.jd.blockchain.utils.io.BytesUtils; | |||||
* @author huanghaiquan | * @author huanghaiquan | ||||
* | * | ||||
*/ | */ | ||||
public class BytesValueEntry implements BytesValue { | |||||
BytesValueType type; | |||||
public class BytesData implements BytesValue { | |||||
DataType type; | |||||
Bytes value; | Bytes value; | ||||
private BytesValueEntry(BytesValueType type, byte[] bytes) { | |||||
private BytesData(DataType type, byte[] bytes) { | |||||
this.type = type; | this.type = type; | ||||
this.value = new Bytes(bytes); | this.value = new Bytes(bytes); | ||||
} | } | ||||
private BytesValueEntry(BytesValueType type, Bytes bytes) { | |||||
private BytesData(DataType type, Bytes bytes) { | |||||
this.type = type; | this.type = type; | ||||
this.value = bytes; | this.value = bytes; | ||||
} | } | ||||
public static BytesValue fromType(BytesValueType type, byte[] value) { | |||||
return new BytesValueEntry(type, value); | |||||
public static BytesValue fromType(DataType type, byte[] value) { | |||||
return new BytesData(type, value); | |||||
} | } | ||||
public static BytesValue fromBytes(byte[] value) { | public static BytesValue fromBytes(byte[] value) { | ||||
return new BytesValueEntry(BytesValueType.BYTES, value); | |||||
return new BytesData(DataType.BYTES, value); | |||||
} | } | ||||
public static BytesValue fromBytes(Bytes value) { | public static BytesValue fromBytes(Bytes value) { | ||||
return new BytesValueEntry(BytesValueType.BYTES, value); | |||||
return new BytesData(DataType.BYTES, value); | |||||
} | } | ||||
public static BytesValue fromImage(byte[] value) { | public static BytesValue fromImage(byte[] value) { | ||||
return new BytesValueEntry(BytesValueType.IMG, value); | |||||
return new BytesData(DataType.IMG, value); | |||||
} | } | ||||
public static BytesValue fromImage(Bytes value) { | public static BytesValue fromImage(Bytes value) { | ||||
return new BytesValueEntry(BytesValueType.IMG, value); | |||||
return new BytesData(DataType.IMG, value); | |||||
} | } | ||||
/** | /** | ||||
@@ -49,7 +49,7 @@ public class BytesValueEntry implements BytesValue { | |||||
* @return | * @return | ||||
*/ | */ | ||||
public static BytesValue fromText(String value) { | public static BytesValue fromText(String value) { | ||||
return new BytesValueEntry(BytesValueType.TEXT, BytesUtils.toBytes(value)); | |||||
return new BytesData(DataType.TEXT, BytesUtils.toBytes(value)); | |||||
} | } | ||||
/** | /** | ||||
@@ -62,51 +62,51 @@ public class BytesValueEntry implements BytesValue { | |||||
if (bytesValue == null) { | if (bytesValue == null) { | ||||
return null; | return null; | ||||
} | } | ||||
if (bytesValue.getType() != BytesValueType.TEXT) { | |||||
throw new ValueTypeCastException("The expected value type is " + BytesValueType.TEXT.toString() | |||||
if (bytesValue.getType() != DataType.TEXT) { | |||||
throw new ValueTypeCastException("The expected value type is " + DataType.TEXT.toString() | |||||
+ ", but it is actually " + bytesValue.getType().toString() + "!"); | + ", but it is actually " + bytesValue.getType().toString() + "!"); | ||||
} | } | ||||
return bytesValue.getValue().toUTF8String(); | return bytesValue.getValue().toUTF8String(); | ||||
} | } | ||||
public static BytesValue fromJSON(String value) { | public static BytesValue fromJSON(String value) { | ||||
return new BytesValueEntry(BytesValueType.JSON, BytesUtils.toBytes(value)); | |||||
return new BytesData(DataType.JSON, BytesUtils.toBytes(value)); | |||||
} | } | ||||
public static BytesValue fromXML(String value) { | public static BytesValue fromXML(String value) { | ||||
return new BytesValueEntry(BytesValueType.XML, BytesUtils.toBytes(value)); | |||||
return new BytesData(DataType.XML, BytesUtils.toBytes(value)); | |||||
} | } | ||||
public static BytesValue fromInt32(int value) { | public static BytesValue fromInt32(int value) { | ||||
return new BytesValueEntry(BytesValueType.INT32, BytesUtils.toBytes(value)); | |||||
return new BytesData(DataType.INT32, BytesUtils.toBytes(value)); | |||||
} | } | ||||
public static BytesValue fromInt64(long value) { | public static BytesValue fromInt64(long value) { | ||||
return new BytesValueEntry(BytesValueType.INT64, BytesUtils.toBytes(value)); | |||||
return new BytesData(DataType.INT64, BytesUtils.toBytes(value)); | |||||
} | } | ||||
public static BytesValue fromInt16(short value) { | public static BytesValue fromInt16(short value) { | ||||
return new BytesValueEntry(BytesValueType.INT16, BytesUtils.toBytes(value)); | |||||
return new BytesData(DataType.INT16, BytesUtils.toBytes(value)); | |||||
} | } | ||||
public static BytesValue fromInt8(byte value) { | public static BytesValue fromInt8(byte value) { | ||||
return new BytesValueEntry(BytesValueType.INT8, BytesUtils.toBytes(value)); | |||||
return new BytesData(DataType.INT8, BytesUtils.toBytes(value)); | |||||
} | } | ||||
public static BytesValue fromTimestamp(long value) { | public static BytesValue fromTimestamp(long value) { | ||||
return new BytesValueEntry(BytesValueType.TIMESTAMP, BytesUtils.toBytes(value)); | |||||
return new BytesData(DataType.TIMESTAMP, BytesUtils.toBytes(value)); | |||||
} | } | ||||
public static BytesValue fromBoolean(boolean value) { | public static BytesValue fromBoolean(boolean value) { | ||||
return new BytesValueEntry(BytesValueType.BOOLEAN, BytesUtils.toBytes(value)); | |||||
return new BytesData(DataType.BOOLEAN, BytesUtils.toBytes(value)); | |||||
} | } | ||||
@Override | @Override | ||||
public BytesValueType getType() { | |||||
public DataType getType() { | |||||
return this.type; | return this.type; | ||||
} | } | ||||
public void setType(BytesValueType type) { | |||||
public void setType(DataType type) { | |||||
this.type = type; | this.type = type; | ||||
} | } | ||||
@@ -0,0 +1,31 @@ | |||||
package com.jd.blockchain.ledger; | |||||
public class BytesDataList implements BytesValueList { | |||||
private BytesValue[] bytesValues; | |||||
public BytesDataList(BytesValue... bytesValues) { | |||||
this.bytesValues = bytesValues; | |||||
} | |||||
@Override | |||||
public BytesValue[] getValues() { | |||||
return bytesValues; | |||||
} | |||||
public static BytesValueList singleText(String value) { | |||||
return new BytesDataList(BytesData.fromText(value)); | |||||
} | |||||
public static BytesValueList singleLong(long value) { | |||||
return new BytesDataList(BytesData.fromInt64(value)); | |||||
} | |||||
public static BytesValueList singleInt(int value) { | |||||
return new BytesDataList(BytesData.fromInt32(value)); | |||||
} | |||||
public static BytesValueList singleBoolean(boolean value) { | |||||
return new BytesDataList(BytesData.fromBoolean(value)); | |||||
} | |||||
} |
@@ -21,7 +21,7 @@ public interface BytesValue { | |||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order = 0, refEnum = true) | @DataField(order = 0, refEnum = true) | ||||
BytesValueType getType(); | |||||
DataType getType(); | |||||
/** | /** | ||||
* 数据值的二进制序列; | * 数据值的二进制序列; | ||||
@@ -0,0 +1,199 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.ledger.resolver.*; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
public class BytesValueEncoding { | |||||
private static final Map<Class<?>, BytesValueResolver> CLASS_RESOLVER_MAP = new ConcurrentHashMap<>(); | |||||
private static final Map<DataType, BytesValueResolver> DATA_TYPE_RESOLVER_MAP = new ConcurrentHashMap<>(); | |||||
static { | |||||
init(); | |||||
} | |||||
private static void init() { | |||||
BytesValueResolver[] resolvers = new BytesValueResolver[]{ | |||||
new BytesToBytesValueResolver(), | |||||
new IntegerToBytesValueResolver(), | |||||
new LongToBytesValueResolver(), | |||||
new ShortToBytesValueResolver(), | |||||
new StringToBytesValueResolver() | |||||
}; | |||||
for (BytesValueResolver currResolver : resolvers) { | |||||
// 填充classMAP | |||||
Class<?>[] supportClasses = currResolver.supportClasses(); | |||||
if (supportClasses != null && supportClasses.length > 0) { | |||||
for (Class<?> clazz : supportClasses) { | |||||
CLASS_RESOLVER_MAP.put(clazz, currResolver); | |||||
} | |||||
} | |||||
// 填充dataTypeMap | |||||
DataType[] supportDataTypes = currResolver.supportDataTypes(); | |||||
if (supportDataTypes != null && supportDataTypes.length > 0) { | |||||
for (DataType dataType : supportDataTypes) { | |||||
DATA_TYPE_RESOLVER_MAP.put(dataType, currResolver); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
public static BytesValue encodeSingle(Object value, Class<?> type) { | |||||
if (value == null) { | |||||
return null; | |||||
} | |||||
if (type == null) { | |||||
type = value.getClass(); | |||||
} else if (type.equals(void.class) || type.equals(Void.class)) { | |||||
return null; | |||||
} | |||||
if (type.isInterface()) { | |||||
// 判断是否含有DataContract注解 | |||||
if (!type.isAnnotationPresent(DataContract.class)) { | |||||
throw new IllegalStateException(String.format("Interface[%s] can not be serialize !!!", type.getName())); | |||||
} | |||||
// 将对象序列化 | |||||
byte[] serialBytes = BinaryProtocol.encode(value, type); | |||||
return BytesData.fromType(DataType.DATA_CONTRACT, serialBytes); | |||||
} | |||||
BytesValueResolver bytesValueResolver = CLASS_RESOLVER_MAP.get(type); | |||||
if (bytesValueResolver == null) { | |||||
throw new IllegalStateException(String.format("Class[%s] can not find encoder !!!", type.getName())); | |||||
} | |||||
return bytesValueResolver.encode(value, type); | |||||
} | |||||
public static BytesValueList encodeArray(Object[] values, Class<?>[] types) { | |||||
if (values == null || values.length == 0) { | |||||
return null; | |||||
} | |||||
if (types != null && types.length != values.length) { | |||||
throw new IllegalStateException("Types can be null, or types's length must be equal BytesValue[]'s !!!"); | |||||
} | |||||
BytesValueListData bytesValueListData = new BytesValueListData(); | |||||
for (int i = 0; i < values.length; i++) { | |||||
BytesValue bytesValue = encodeSingle(values[i], types == null ? null : types[i]); | |||||
bytesValueListData.add(bytesValue); | |||||
} | |||||
return bytesValueListData; | |||||
} | |||||
public static Object decode(BytesValue value) { | |||||
return decode(value, null); | |||||
} | |||||
public static Object decode(BytesValue value, Class<?> type) { | |||||
DataType dataType = value.getType(); | |||||
BytesValueResolver valueResolver = DATA_TYPE_RESOLVER_MAP.get(dataType); | |||||
if (valueResolver == null) { | |||||
throw new IllegalStateException(String.format("DataType[%s] can not find encoder !!!", dataType.name())); | |||||
} | |||||
return type == null ? valueResolver.decode(value) : valueResolver.decode(value, type); | |||||
} | |||||
public static Object[] decode(BytesValueList values, Class<?>[] types) { | |||||
BytesValue[] bytesValues = values.getValues(); | |||||
if (bytesValues == null || bytesValues.length == 0) { | |||||
return null; | |||||
} | |||||
// 允许types为null,此时每个BytesValue按照当前的对象来处理 | |||||
// 若types不为null,则types's长度必须和bytesValues一致 | |||||
if (types != null && types.length != bytesValues.length) { | |||||
throw new IllegalStateException("Types can be null, or types's length must be equal BytesValue[]'s !!!"); | |||||
} | |||||
Object[] resolveObjs = new Object[bytesValues.length]; | |||||
if (types == null) { | |||||
// 按照默认方式解析 | |||||
for (int i = 0; i < bytesValues.length; i++) { | |||||
BytesValue bytesValue = bytesValues[i]; | |||||
DataType dataType = bytesValue.getType(); | |||||
BytesValueResolver valueResolver = DATA_TYPE_RESOLVER_MAP.get(dataType); | |||||
if (valueResolver == null) { | |||||
throw new IllegalStateException(String.format("DataType[%s] can not find encoder !!!", dataType.name())); | |||||
} | |||||
resolveObjs[i] = valueResolver.decode(bytesValue); | |||||
} | |||||
return resolveObjs; | |||||
} | |||||
// 按照输入的Class进行解析 | |||||
for (int i = 0; i < bytesValues.length; i++) { | |||||
resolveObjs[i] = decode(bytesValues[i], types[i]); | |||||
} | |||||
return resolveObjs; | |||||
} | |||||
public static Object getDefaultValue(Class<?> type) { | |||||
if (type == void.class || type == Void.class) { | |||||
return null; | |||||
} | |||||
if (!type.isPrimitive()) { | |||||
// 非基本类型 | |||||
return null; | |||||
} else { | |||||
// 基本类型需要处理返回值,目前采用枚举遍历方式 | |||||
// 八种基本类型:int, double, float, long, short, boolean, byte, char, void | |||||
if (type.equals(int.class)) { | |||||
return 0; | |||||
} else if (type.equals(double.class)) { | |||||
return 0.0D; | |||||
} else if (type.equals(float.class)) { | |||||
return 0F; | |||||
} else if (type.equals(long.class)) { | |||||
return 0L; | |||||
} else if (type.equals(short.class)) { | |||||
return (short) 0; | |||||
} else if (type.equals(boolean.class)) { | |||||
return Boolean.FALSE; | |||||
} else if (type.equals(byte.class)) { | |||||
return (byte) 0; | |||||
} else if (type.equals(char.class)) { | |||||
return (char) 0; | |||||
} | |||||
return null; | |||||
} | |||||
} | |||||
public static boolean supportType(Class<?> currParamType) { | |||||
// 支持空返回值 | |||||
if (currParamType.equals(void.class) || currParamType.equals(Void.class)) { | |||||
return true; | |||||
} | |||||
if (currParamType.isInterface()) { | |||||
// 接口序列化必须实现DataContract注解 | |||||
if (!currParamType.isAnnotationPresent(DataContract.class)) { | |||||
throw new IllegalStateException(String.format("Interface[%s] can not be serialize !!!", currParamType.getName())); | |||||
} | |||||
return true; | |||||
} | |||||
return CLASS_RESOLVER_MAP.containsKey(currParamType); | |||||
} | |||||
public static class BytesValueListData implements BytesValueList { | |||||
private List<BytesValue> bytesValues = new ArrayList<>(); | |||||
public void add(BytesValue bytesValue) { | |||||
bytesValues.add(bytesValue); | |||||
} | |||||
@Override | |||||
public BytesValue[] getValues() { | |||||
BytesValue[] bytesValueArray = new BytesValue[bytesValues.size()]; | |||||
return bytesValues.toArray(bytesValueArray); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,13 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
@DataContract(code = DataCodes.BYTES_VALUE_LIST) | |||||
public interface BytesValueList { | |||||
@DataField(order = 0, refContract = true, list = true) | |||||
BytesValue[] getValues(); | |||||
} |
@@ -1,20 +0,0 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
import java.math.BigDecimal; | |||||
/** | |||||
* contract args for BIG_INT; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_BIG_INT) | |||||
public interface CONTRACT_BIG_INT { | |||||
@DataField(order=2, primitiveType= PrimitiveType.BIG_INT) | |||||
BigDecimal getValue(); | |||||
} |
@@ -1,19 +0,0 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
/** | |||||
* contract args for Binary; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_BINARY) | |||||
public interface CONTRACT_BINARY { | |||||
@DataField(order=2, primitiveType= PrimitiveType.BYTES) | |||||
Bytes getValue(); | |||||
} |
@@ -1,18 +0,0 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
/** | |||||
* contract args for int16; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_INT16) | |||||
public interface CONTRACT_INT16 { | |||||
@DataField(order=2, primitiveType= PrimitiveType.INT16) | |||||
short getValue(); | |||||
} |
@@ -1,18 +0,0 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
/** | |||||
* contract args for int32; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_INT32) | |||||
public interface CONTRACT_INT32 { | |||||
@DataField(order=2, primitiveType= PrimitiveType.INT32) | |||||
int getValue(); | |||||
} |
@@ -1,18 +0,0 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
/** | |||||
* contract args for int64; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_INT64) | |||||
public interface CONTRACT_INT64 { | |||||
@DataField(order=2, primitiveType= PrimitiveType.INT64) | |||||
long getValue(); | |||||
} |
@@ -1,18 +0,0 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
/** | |||||
* contract args for int8; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_INT8) | |||||
public interface CONTRACT_INT8 { | |||||
@DataField(order=2, primitiveType= PrimitiveType.INT8) | |||||
Byte getValue(); | |||||
} |
@@ -1,18 +0,0 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
/** | |||||
* contract args for String; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_TEXT) | |||||
public interface CONTRACT_TEXT { | |||||
@DataField(order=2, primitiveType= PrimitiveType.TEXT) | |||||
String getValue(); | |||||
} |
@@ -1,14 +0,0 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
import java.lang.reflect.Method; | |||||
/** | |||||
* contract's args; | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_ARGS) | |||||
public interface ContractArgs { | |||||
Method getMethod(); | |||||
Object[] getArgs(); | |||||
} |
@@ -1,20 +0,0 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
/** | |||||
* build complex param Object, provide more String attributes; | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_BIZ_CONTENT) | |||||
public interface ContractBizContent { | |||||
/** | |||||
* param lists; | |||||
* @return | |||||
*/ | |||||
@DataField(order = 1, list = true, primitiveType = PrimitiveType.TEXT, genericContract = true) | |||||
String[] getAttrs(); | |||||
} |
@@ -7,6 +7,8 @@ import com.jd.blockchain.consts.DataCodes; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
/** | /** | ||||
* 发送合约事件的操作; | |||||
* | |||||
* @author huanghaiquan | * @author huanghaiquan | ||||
* | * | ||||
*/ | */ | ||||
@@ -34,15 +36,7 @@ public interface ContractEventSendOperation extends Operation { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order = 4, primitiveType = PrimitiveType.BYTES) | |||||
byte[] getArgs(); | |||||
/** | |||||
* 获得交易操作时间; | |||||
* | |||||
* @return | |||||
*/ | |||||
@DataField(order = 5, primitiveType = PrimitiveType.INT64) | |||||
long getTxOpTime(); | |||||
@DataField(order = 4, refContract = true) | |||||
BytesValueList getArgs(); | |||||
} | } |
@@ -0,0 +1,13 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
@DataContract(code= DataCodes.CONTRACT) | |||||
public interface ContractInfo extends AccountHeader { | |||||
@DataField(order=4, primitiveType= PrimitiveType.BYTES) | |||||
byte[] getChainCode(); | |||||
} |
@@ -7,7 +7,7 @@ import com.jd.blockchain.consts.DataCodes; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
@DataContract(code= DataCodes.TX_OP_DATA_ACC_SET) | @DataContract(code= DataCodes.TX_OP_DATA_ACC_SET) | ||||
public interface DataAccountKVSetOperation extends Operation{ | |||||
public interface DataAccountKVSetOperation extends Operation { | |||||
@DataField(order=2, primitiveType=PrimitiveType.BYTES) | @DataField(order=2, primitiveType=PrimitiveType.BYTES) | ||||
Bytes getAccountAddress(); | Bytes getAccountAddress(); | ||||
@@ -1,8 +1,9 @@ | |||||
package com.jd.blockchain.ledger; | package com.jd.blockchain.ledger; | ||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.binaryproto.BaseType; | |||||
import com.jd.blockchain.binaryproto.EnumContract; | import com.jd.blockchain.binaryproto.EnumContract; | ||||
import com.jd.blockchain.binaryproto.EnumField; | import com.jd.blockchain.binaryproto.EnumField; | ||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | import com.jd.blockchain.consts.DataCodes; | ||||
/** | /** | ||||
@@ -11,8 +12,8 @@ import com.jd.blockchain.consts.DataCodes; | |||||
* @author huanghaiquan | * @author huanghaiquan | ||||
* | * | ||||
*/ | */ | ||||
@EnumContract(code = DataCodes.ENUM_TYPE_BYTES_VALUE_TYPE, name = "BytesValueType", decription = "") | |||||
public enum BytesValueType { | |||||
@EnumContract(code = DataCodes.ENUM_TYPE_BYTES_VALUE_TYPE) | |||||
public enum DataType { | |||||
/** | /** | ||||
* 空; | * 空; | ||||
@@ -37,59 +38,84 @@ public enum BytesValueType { | |||||
INT64(PrimitiveType.INT64.CODE), | INT64(PrimitiveType.INT64.CODE), | ||||
/** | /** | ||||
* 时间戳; | |||||
* 文本数据; | |||||
*/ | */ | ||||
TIMESTAMP(PrimitiveType.TIMESTAMP.CODE), | |||||
TEXT(PrimitiveType.TEXT.CODE), | |||||
/** | /** | ||||
* 文本数据; | |||||
* 二进制数据; | |||||
*/ | */ | ||||
TEXT(PrimitiveType.TEXT.CODE), | |||||
BYTES(PrimitiveType.BYTES.CODE), | |||||
/** | /** | ||||
* 文本数据; | |||||
* 时间戳; | |||||
*/ | */ | ||||
JSON(PrimitiveType.JSON.CODE), | |||||
TIMESTAMP((byte) (BaseType.INTEGER | 0x08)), | |||||
/** | /** | ||||
* 文本数据; | * 文本数据; | ||||
*/ | */ | ||||
XML(PrimitiveType.XML.CODE), | |||||
JSON((byte) (BaseType.TEXT | 0x01)), | |||||
/** | /** | ||||
* 二进制数据; | |||||
* 文本数据; | |||||
*/ | */ | ||||
BYTES(PrimitiveType.BYTES.CODE), | |||||
XML((byte) (BaseType.TEXT | 0x02)), | |||||
/** | /** | ||||
* 大整数; | * 大整数; | ||||
*/ | */ | ||||
BIG_INT(PrimitiveType.BIG_INT.CODE), | |||||
BIG_INT((byte) (BaseType.BYTES | 0x01)), | |||||
/** | /** | ||||
* 图片; | * 图片; | ||||
*/ | */ | ||||
IMG(PrimitiveType.IMG.CODE), | |||||
IMG((byte) (BaseType.BYTES | 0x02)), | |||||
/** | /** | ||||
* 视频; | * 视频; | ||||
*/ | */ | ||||
VIDEO(PrimitiveType.VIDEO.CODE), | |||||
VIDEO((byte) (BaseType.BYTES | 0x03)), | |||||
/** | |||||
* 位置坐标; | |||||
*/ | |||||
LOCATION((byte) (BaseType.BYTES | 0x04)), | |||||
/** | |||||
* 公钥; | |||||
*/ | |||||
PUB_KEY((byte) (BaseType.BYTES | 0x05)), | |||||
/** | |||||
* 签名摘要; | |||||
*/ | |||||
SIGNATURE_DIGEST((byte) (BaseType.BYTES | 0x06)), | |||||
/** | |||||
* 哈希摘要; | |||||
*/ | |||||
HASH_DIGEST((byte) (BaseType.BYTES | 0x07)), | |||||
/** | |||||
* 加密数据; | |||||
*/ | |||||
ENCRYPTED_DATA((byte) (BaseType.BYTES | 0x08)), | |||||
/** | /** | ||||
* 位置; | |||||
* DataContract 数据; | |||||
*/ | */ | ||||
LOCATION(PrimitiveType.LOCATION.CODE); | |||||
DATA_CONTRACT((byte) (BaseType.EXT | 0x01)); | |||||
@EnumField(type = PrimitiveType.INT8) | @EnumField(type = PrimitiveType.INT8) | ||||
public final byte CODE; | public final byte CODE; | ||||
private BytesValueType(byte code) { | |||||
private DataType(byte code) { | |||||
this.CODE = code; | this.CODE = code; | ||||
} | } | ||||
public static BytesValueType valueOf(byte code) { | |||||
for (BytesValueType dataType : BytesValueType.values()) { | |||||
public static DataType valueOf(byte code) { | |||||
for (DataType dataType : DataType.values()) { | |||||
if (dataType.CODE == code) { | if (dataType.CODE == code) { | ||||
return dataType; | return dataType; | ||||
} | } |
@@ -25,7 +25,7 @@ public interface KVDataEntry { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
BytesValueType getType(); | |||||
DataType getType(); | |||||
/** | /** | ||||
* 值; | * 值; | ||||
@@ -58,8 +58,8 @@ public class KVDataObject implements KVDataEntry { | |||||
* @see com.jd.blockchain.ledger.KVDataEntry#getType() | * @see com.jd.blockchain.ledger.KVDataEntry#getType() | ||||
*/ | */ | ||||
@Override | @Override | ||||
public BytesValueType getType() { | |||||
return bytesValue == null ? BytesValueType.NIL : bytesValue.getType(); | |||||
public DataType getType() { | |||||
return bytesValue == null ? DataType.NIL : bytesValue.getType(); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -95,7 +95,7 @@ public class KVDataObject implements KVDataEntry { | |||||
* @return | * @return | ||||
*/ | */ | ||||
public boolean isNil() { | public boolean isNil() { | ||||
return bytesValue == null || BytesValueType.NIL == bytesValue.getType(); | |||||
return bytesValue == null || DataType.NIL == bytesValue.getType(); | |||||
} | } | ||||
/** | /** | ||||
@@ -119,10 +119,10 @@ public class KVDataObject implements KVDataEntry { | |||||
* @return | * @return | ||||
*/ | */ | ||||
public byte tinyValue() { | public byte tinyValue() { | ||||
if (BytesValueType.INT8 == getType()) { | |||||
if (DataType.INT8 == getType()) { | |||||
return bytesValue.getValue().toBytes()[0]; | return bytesValue.getValue().toBytes()[0]; | ||||
} | } | ||||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", BytesValueType.INT8, getType())); | |||||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", DataType.INT8, getType())); | |||||
} | } | ||||
/** | /** | ||||
@@ -137,10 +137,10 @@ public class KVDataObject implements KVDataEntry { | |||||
* @return | * @return | ||||
*/ | */ | ||||
public short shortValue() { | public short shortValue() { | ||||
if (BytesValueType.INT16 == getType()) { | |||||
if (DataType.INT16 == getType()) { | |||||
return BytesUtils.toShort(bytesValue.getValue().toBytes(), 0); | return BytesUtils.toShort(bytesValue.getValue().toBytes(), 0); | ||||
} | } | ||||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", BytesValueType.INT16, getType())); | |||||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", DataType.INT16, getType())); | |||||
} | } | ||||
/** | /** | ||||
@@ -155,10 +155,10 @@ public class KVDataObject implements KVDataEntry { | |||||
* @return | * @return | ||||
*/ | */ | ||||
public int intValue() { | public int intValue() { | ||||
if (BytesValueType.INT32 == getType()) { | |||||
if (DataType.INT32 == getType()) { | |||||
return BytesUtils.toInt(bytesValue.getValue().toBytes(), 0); | return BytesUtils.toInt(bytesValue.getValue().toBytes(), 0); | ||||
} | } | ||||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", BytesValueType.INT32, getType())); | |||||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", DataType.INT32, getType())); | |||||
} | } | ||||
/** | /** | ||||
@@ -173,10 +173,10 @@ public class KVDataObject implements KVDataEntry { | |||||
* @return | * @return | ||||
*/ | */ | ||||
public long longValue() { | public long longValue() { | ||||
if (BytesValueType.INT64 == getType()) { | |||||
if (DataType.INT64 == getType()) { | |||||
return BytesUtils.toLong(bytesValue.getValue().toBytes(), 0); | return BytesUtils.toLong(bytesValue.getValue().toBytes(), 0); | ||||
} | } | ||||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", BytesValueType.INT64, getType())); | |||||
throw new IllegalStateException(String.format("Expected type [%s], but [%s]", DataType.INT64, getType())); | |||||
} | } | ||||
@@ -192,11 +192,11 @@ public class KVDataObject implements KVDataEntry { | |||||
* @return | * @return | ||||
*/ | */ | ||||
public BigInteger bigIntValue() { | public BigInteger bigIntValue() { | ||||
if (BytesValueType.BIG_INT == getType()) { | |||||
if (DataType.BIG_INT == getType()) { | |||||
return new BigInteger(bytesValue.getValue().toBytes()); | return new BigInteger(bytesValue.getValue().toBytes()); | ||||
} | } | ||||
throw new IllegalStateException( | throw new IllegalStateException( | ||||
String.format("Expected type [%s], but [%s]", BytesValueType.BIG_INT, getType())); | |||||
String.format("Expected type [%s], but [%s]", DataType.BIG_INT, getType())); | |||||
} | } | ||||
/** | /** | ||||
@@ -211,11 +211,11 @@ public class KVDataObject implements KVDataEntry { | |||||
* @return | * @return | ||||
*/ | */ | ||||
public boolean boolValue() { | public boolean boolValue() { | ||||
if (BytesValueType.BOOLEAN == getType()) { | |||||
if (DataType.BOOLEAN == getType()) { | |||||
return BytesUtils.toBoolean(bytesValue.getValue().toBytes()[0]); | return BytesUtils.toBoolean(bytesValue.getValue().toBytes()[0]); | ||||
} | } | ||||
throw new IllegalStateException( | throw new IllegalStateException( | ||||
String.format("Expected type [%s], but [%s]", BytesValueType.BOOLEAN, getType())); | |||||
String.format("Expected type [%s], but [%s]", DataType.BOOLEAN, getType())); | |||||
} | } | ||||
/** | /** | ||||
@@ -230,12 +230,12 @@ public class KVDataObject implements KVDataEntry { | |||||
* @return | * @return | ||||
*/ | */ | ||||
public Date datetimeValue() { | public Date datetimeValue() { | ||||
if (BytesValueType.TIMESTAMP == getType()) { | |||||
if (DataType.TIMESTAMP == getType()) { | |||||
long ts = BytesUtils.toLong(bytesValue.getValue().toBytes()); | long ts = BytesUtils.toLong(bytesValue.getValue().toBytes()); | ||||
return new Date(ts); | return new Date(ts); | ||||
} | } | ||||
throw new IllegalStateException( | throw new IllegalStateException( | ||||
String.format("Expected type [%s], but [%s]", BytesValueType.TIMESTAMP, getType())); | |||||
String.format("Expected type [%s], but [%s]", DataType.TIMESTAMP, getType())); | |||||
} | } | ||||
/** | /** | ||||
@@ -251,12 +251,12 @@ public class KVDataObject implements KVDataEntry { | |||||
* @return | * @return | ||||
*/ | */ | ||||
public String stringValue() { | public String stringValue() { | ||||
BytesValueType type = getType(); | |||||
if (BytesValueType.TEXT == type || BytesValueType.JSON == type || BytesValueType.XML == type) { | |||||
DataType type = getType(); | |||||
if (DataType.TEXT == type || DataType.JSON == type || DataType.XML == type) { | |||||
return bytesValue.getValue().toUTF8String(); | return bytesValue.getValue().toUTF8String(); | ||||
} | } | ||||
throw new IllegalStateException(String.format("Expected type [%s] or [%s] or [%s] , but [%s]", | throw new IllegalStateException(String.format("Expected type [%s] or [%s] or [%s] , but [%s]", | ||||
PrimitiveType.TEXT, BytesValueType.JSON, BytesValueType.XML, type)); | |||||
PrimitiveType.TEXT, DataType.JSON, DataType.XML, type)); | |||||
} | } | ||||
// // ---------------- | // // ---------------- | ||||
@@ -8,6 +8,14 @@ public class KVDataVO { | |||||
private String key; | private String key; | ||||
private long[] version; | private long[] version; | ||||
public KVDataVO() { | |||||
} | |||||
public KVDataVO(String key, long[] version) { | |||||
this.key = key; | |||||
this.version = version; | |||||
} | |||||
public String getKey() { | public String getKey() { | ||||
return key; | return key; | ||||
} | } | ||||
@@ -8,6 +8,13 @@ package com.jd.blockchain.ledger; | |||||
public class KVInfoVO { | public class KVInfoVO { | ||||
private KVDataVO[] data; | private KVDataVO[] data; | ||||
public KVInfoVO() { | |||||
} | |||||
public KVInfoVO(KVDataVO[] data) { | |||||
this.data = data; | |||||
} | |||||
public KVDataVO[] getData() { | public KVDataVO[] getData() { | ||||
return data; | return data; | ||||
} | } | ||||
@@ -0,0 +1,16 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
@DataContract(code = DataCodes.TX_OP_RESULT) | |||||
public interface OperationResult { | |||||
@DataField(order = 1, primitiveType = PrimitiveType.INT32) | |||||
int getIndex(); | |||||
@DataField(order = 2, refContract = true) | |||||
BytesValue getResult(); | |||||
} |
@@ -0,0 +1,35 @@ | |||||
package com.jd.blockchain.ledger; | |||||
public class OperationResultData implements OperationResult { | |||||
private int index; | |||||
private BytesValue result; | |||||
public OperationResultData() { | |||||
} | |||||
public OperationResultData(int index, BytesValue result) { | |||||
this.index = index; | |||||
this.result = result; | |||||
} | |||||
@Override | |||||
public int getIndex() { | |||||
return index; | |||||
} | |||||
@Override | |||||
public BytesValue getResult() { | |||||
return result; | |||||
} | |||||
public void setIndex(int index) { | |||||
this.index = index; | |||||
} | |||||
public void setResult(BytesValue result) { | |||||
this.result = result; | |||||
} | |||||
} |
@@ -1,8 +1,9 @@ | |||||
package com.jd.blockchain.ledger; | package com.jd.blockchain.ledger; | ||||
import java.io.Closeable; | |||||
import com.jd.blockchain.crypto.AsymmetricKeypair; | import com.jd.blockchain.crypto.AsymmetricKeypair; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.crypto.SignatureFunction; | |||||
/** | /** | ||||
* 已就绪的交易; | * 已就绪的交易; | ||||
@@ -10,7 +11,7 @@ import com.jd.blockchain.crypto.SignatureFunction; | |||||
* @author huanghaiquan | * @author huanghaiquan | ||||
* | * | ||||
*/ | */ | ||||
public interface PreparedTransaction extends HashObject { | |||||
public interface PreparedTransaction extends HashObject, Closeable { | |||||
/** | /** | ||||
* 交易内容的 Hash; | * 交易内容的 Hash; | ||||
@@ -33,10 +34,8 @@ public interface PreparedTransaction extends HashObject { | |||||
/** | /** | ||||
* 对交易进行签名; | * 对交易进行签名; | ||||
* | * | ||||
* @param address | |||||
* 签名账户的地址; | |||||
* @param privKey | |||||
* 签名账户的私钥; | |||||
* @param address 签名账户的地址; | |||||
* @param privKey 签名账户的私钥; | |||||
* @return | * @return | ||||
*/ | */ | ||||
DigitalSignature sign(AsymmetricKeypair keyPair); | DigitalSignature sign(AsymmetricKeypair keyPair); | ||||
@@ -44,17 +43,18 @@ public interface PreparedTransaction extends HashObject { | |||||
/** | /** | ||||
* 加入签名; | * 加入签名; | ||||
* | * | ||||
* @param address | |||||
* 签名账户的地址; | |||||
* @param digest | |||||
* Base64格式的签名摘要; | |||||
* @param address 签名账户的地址; | |||||
* @param digest Base64格式的签名摘要; | |||||
* @return | * @return | ||||
*/ | */ | ||||
void addSignature(DigitalSignature signature); | void addSignature(DigitalSignature signature); | ||||
/** | /** | ||||
* 生成交易请求; | |||||
* 提交交易请求到共识节点;<br> | |||||
* | |||||
* 这是同步方法,将阻塞当前线程,直到交易处理完成并返回结果之后,此方法才返回给调用者; | |||||
* | * | ||||
*/ | */ | ||||
TransactionResponse commit(); | TransactionResponse commit(); | ||||
} | } |
@@ -5,7 +5,6 @@ import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | import com.jd.blockchain.binaryproto.PrimitiveType; | ||||
import com.jd.blockchain.consts.DataCodes; | import com.jd.blockchain.consts.DataCodes; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.utils.io.ByteArray; | |||||
/** | /** | ||||
* Transaction 区块链交易,是被原子执行的操作集合; | * Transaction 区块链交易,是被原子执行的操作集合; | ||||
@@ -13,7 +12,7 @@ import com.jd.blockchain.utils.io.ByteArray; | |||||
* @author huanghaiquan | * @author huanghaiquan | ||||
* | * | ||||
*/ | */ | ||||
@DataContract(code= DataCodes.TX) | |||||
@DataContract(code = DataCodes.TX) | |||||
public interface Transaction extends NodeRequest, HashObject { | public interface Transaction extends NodeRequest, HashObject { | ||||
/** | /** | ||||
@@ -23,7 +22,7 @@ public interface Transaction extends NodeRequest, HashObject { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order=1, primitiveType = PrimitiveType.BYTES) | |||||
@DataField(order = 1, primitiveType = PrimitiveType.BYTES) | |||||
@Override | @Override | ||||
HashDigest getHash(); | HashDigest getHash(); | ||||
@@ -32,7 +31,7 @@ public interface Transaction extends NodeRequest, HashObject { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order=2, primitiveType=PrimitiveType.INT64) | |||||
@DataField(order = 2, primitiveType = PrimitiveType.INT64) | |||||
long getBlockHeight(); | long getBlockHeight(); | ||||
/** | /** | ||||
@@ -42,7 +41,14 @@ public interface Transaction extends NodeRequest, HashObject { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order=3, refEnum=true) | |||||
@DataField(order = 3, refEnum = true) | |||||
TransactionState getExecutionState(); | TransactionState getExecutionState(); | ||||
/** | |||||
* 交易的返回结果 | |||||
* | |||||
* @return | |||||
*/ | |||||
@DataField(order=4, list = true, refContract=true) | |||||
OperationResult[] getOperationResults(); | |||||
} | } |
@@ -27,7 +27,7 @@ public class TransactionRespHandle implements TransactionResponse { | |||||
private TransactionState globalResult; | private TransactionState globalResult; | ||||
private String[] contractReturn; | |||||
private OperationResult[] operationResults; | |||||
public TransactionRespHandle(TransactionRequest request, TransactionState result, TransactionState globalResult) { | public TransactionRespHandle(TransactionRequest request, TransactionState result, TransactionState globalResult) { | ||||
this.request = request; | this.request = request; | ||||
@@ -51,8 +51,8 @@ public class TransactionRespHandle implements TransactionResponse { | |||||
this.result = result; | this.result = result; | ||||
} | } | ||||
public void setContractReturn(String[] contractReturn) { | |||||
this.contractReturn = contractReturn; | |||||
public void setOperationResults(OperationResult[] operationResults) { | |||||
this.operationResults = operationResults; | |||||
} | } | ||||
public LedgerBlock getBlock() { | public LedgerBlock getBlock() { | ||||
@@ -97,7 +97,7 @@ public class TransactionRespHandle implements TransactionResponse { | |||||
} | } | ||||
@Override | @Override | ||||
public String[] getContractReturn() { | |||||
return contractReturn; | |||||
public OperationResult[] getOperationResults() { | |||||
return operationResults; | |||||
} | } | ||||
} | } |
@@ -12,7 +12,7 @@ import com.jd.blockchain.crypto.HashDigest; | |||||
* @author huanghaiquan | * @author huanghaiquan | ||||
* | * | ||||
*/ | */ | ||||
@DataContract(code= DataCodes.TX_RESPONSE) | |||||
@DataContract(code = DataCodes.TX_RESPONSE) | |||||
public interface TransactionResponse { | public interface TransactionResponse { | ||||
/** | /** | ||||
@@ -20,7 +20,7 @@ public interface TransactionResponse { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order=1, primitiveType = PrimitiveType.BYTES) | |||||
@DataField(order = 1, primitiveType = PrimitiveType.BYTES) | |||||
HashDigest getContentHash(); | HashDigest getContentHash(); | ||||
/** | /** | ||||
@@ -28,7 +28,7 @@ public interface TransactionResponse { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order=2, refEnum=true) | |||||
@DataField(order = 2, refEnum = true) | |||||
TransactionState getExecutionState(); | TransactionState getExecutionState(); | ||||
/** | /** | ||||
@@ -36,7 +36,7 @@ public interface TransactionResponse { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order=3, primitiveType = PrimitiveType.BYTES) | |||||
@DataField(order = 3, primitiveType = PrimitiveType.BYTES) | |||||
HashDigest getBlockHash(); | HashDigest getBlockHash(); | ||||
/** | /** | ||||
@@ -47,7 +47,7 @@ public interface TransactionResponse { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order=4, primitiveType=PrimitiveType.INT64) | |||||
@DataField(order = 4, primitiveType = PrimitiveType.INT64) | |||||
long getBlockHeight(); | long getBlockHeight(); | ||||
/** | /** | ||||
@@ -55,7 +55,7 @@ public interface TransactionResponse { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order=5, primitiveType=PrimitiveType.BOOLEAN) | |||||
@DataField(order = 5, primitiveType = PrimitiveType.BOOLEAN) | |||||
boolean isSuccess(); | boolean isSuccess(); | ||||
/** | /** | ||||
@@ -63,6 +63,6 @@ public interface TransactionResponse { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order=6, list=true, primitiveType=PrimitiveType.TEXT) | |||||
String[] getContractReturn(); | |||||
@DataField(order=6, list=true, refContract = true) | |||||
OperationResult[] getOperationResults(); | |||||
} | } |
@@ -1,5 +1,7 @@ | |||||
package com.jd.blockchain.ledger; | package com.jd.blockchain.ledger; | ||||
import java.io.Closeable; | |||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.transaction.ClientOperator; | import com.jd.blockchain.transaction.ClientOperator; | ||||
@@ -9,7 +11,7 @@ import com.jd.blockchain.transaction.ClientOperator; | |||||
* @author huanghaiquan | * @author huanghaiquan | ||||
* | * | ||||
*/ | */ | ||||
public interface TransactionTemplate extends ClientOperator { | |||||
public interface TransactionTemplate extends ClientOperator, Closeable { | |||||
HashDigest getLedgerHash(); | HashDigest getLedgerHash(); | ||||
@@ -0,0 +1,54 @@ | |||||
package com.jd.blockchain.ledger.resolver; | |||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.ledger.DataType; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
public abstract class AbstractBytesValueResolver implements BytesValueResolver { | |||||
protected boolean isSupport(Class<?> type) { | |||||
if (type == null) { | |||||
return false; | |||||
} | |||||
Class<?>[] supports = supportClasses(); | |||||
if (supports != null && supports.length > 0) { | |||||
for (Class<?> clazz : supports) { | |||||
if (type.equals(clazz)) { | |||||
return true; | |||||
} | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
protected boolean isSupport(DataType dataType) { | |||||
if (dataType == null) { | |||||
return false; | |||||
} | |||||
DataType[] supports = supportDataTypes(); | |||||
if (supports != null && supports.length > 0) { | |||||
for (DataType dt : supports) { | |||||
if (dataType.equals(dt)) { | |||||
return true; | |||||
} | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
@Override | |||||
public BytesValue encode(Object value) { | |||||
return encode(value, value.getClass()); | |||||
} | |||||
@Override | |||||
public Object decode(BytesValue value) { | |||||
DataType dataType = value.getType(); | |||||
if (!isSupport(dataType)) { | |||||
throw new IllegalStateException(String.format("Un-support encode DataType[%s] Object !!!", dataType.name())); | |||||
} | |||||
return decode(value.getValue()); | |||||
} | |||||
protected abstract Object decode(Bytes value); | |||||
} |
@@ -0,0 +1,58 @@ | |||||
package com.jd.blockchain.ledger.resolver; | |||||
import com.jd.blockchain.ledger.BytesData; | |||||
import com.jd.blockchain.ledger.BytesValue; | |||||
import com.jd.blockchain.ledger.DataType; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
import java.util.Set; | |||||
public class BytesToBytesValueResolver extends AbstractBytesValueResolver { | |||||
private final Class<?>[] supportClasses = {Bytes.class, byte[].class}; | |||||
private final DataType[] supportDataTypes = {DataType.BYTES}; | |||||
private final Set<Class<?>> convertClasses = initByteConvertSet(); | |||||
@Override | |||||
public BytesValue encode(Object value, Class<?> type) { | |||||
if (!isSupport(type)) { | |||||
throw new IllegalStateException(String.format("Un-support encode Class[%s] Object !!!", type.getName())); | |||||
} | |||||
if (type.equals(byte[].class)) { | |||||
return BytesData.fromBytes((byte[]) value); | |||||
} | |||||
return BytesData.fromBytes((Bytes) value); | |||||
} | |||||
@Override | |||||
public Class<?>[] supportClasses() { | |||||
return supportClasses; | |||||
} | |||||
@Override | |||||
public DataType[] supportDataTypes() { | |||||
return supportDataTypes; | |||||
} | |||||
@Override | |||||
protected Object decode(Bytes value) { | |||||
return value; | |||||
} | |||||
@Override | |||||
public Object decode(BytesValue value, Class<?> clazz) { | |||||
Bytes bytesVal = (Bytes) decode(value); | |||||
if (!convertClasses.contains(clazz)) { | |||||
throw new IllegalStateException(String.format("Un-Support decode value to class[%s] !!!", clazz.getName())); | |||||
} | |||||
if (clazz.equals(String.class)) { | |||||
return bytesVal.toUTF8String(); | |||||
} else if (clazz.equals(byte[].class)) { | |||||
return bytesVal.toBytes(); | |||||
} | |||||
return bytesVal; | |||||
} | |||||
} |