# 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 static final int BYTES_VALUE = 0x80; | |||
public static final int BYTES_VALUE_LIST = 0x81; | |||
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_LEDGER = 0x110; | |||
public static final int BLOCK_GENESIS = 0x120; | |||
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_RETURN_MESSAGE = 0x230; | |||
public static final int TX_OP = 0x300; | |||
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_OP_RESULT = 0x360; | |||
public static final int METADATA = 0x600; | |||
public static final int METADATA_INIT_SETTING = 0x610; | |||
@@ -81,17 +85,9 @@ public interface DataCodes { | |||
//contract related; | |||
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_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; | |||
@@ -1,16 +1,14 @@ | |||
package com.jd.blockchain.contract.engine; | |||
import com.jd.blockchain.contract.ContractEventContext; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.utils.Bytes; | |||
import java.util.concurrent.CompletableFuture; | |||
import java.util.concurrent.Future; | |||
public interface ContractCode { | |||
Bytes getAddress(); | |||
long getVersion(); | |||
void processEvent(ContractEventContext eventContext, CompletableFuture<String> execReturn); | |||
BytesValue processEvent(ContractEventContext eventContext); | |||
} |
@@ -1,6 +1,5 @@ | |||
package com.jd.blockchain.contract.engine; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.utils.Bytes; | |||
/** | |||
@@ -27,9 +26,9 @@ public interface ContractEngine { | |||
* 如果已经存在,则直接返回已有实例; | |||
* | |||
* @param address | |||
* @param code | |||
* @param codeBytes | |||
* @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.ContractEngine; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.runtime.Module; | |||
import com.jd.blockchain.runtime.RuntimeContext; | |||
import com.jd.blockchain.utils.Bytes; | |||
@@ -1,42 +1,60 @@ | |||
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.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 Module codeModule; | |||
private Bytes address; | |||
private long version; | |||
private ContractEventContext contractEventContext; | |||
private ContractType contractType; | |||
public JavaContractCode(Bytes address, long version, Module codeModule) { | |||
super(address, version, resolveContractDefinition(codeModule)); | |||
this.address = address; | |||
this.version = version; | |||
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 | |||
public Bytes getAddress() { | |||
return address; | |||
@@ -48,87 +66,42 @@ public class JavaContractCode implements ContractCode { | |||
} | |||
@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.MethodDeclaration; | |||
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 org.apache.maven.plugin.AbstractMojo; | |||
import org.apache.maven.plugin.MojoFailureException; | |||
@@ -1,6 +1,5 @@ | |||
package com.jd.blockchain.contract; | |||
import com.jd.blockchain.ledger.ContractBizContent; | |||
import com.jd.blockchain.utils.Bytes; | |||
/** | |||
@@ -1,6 +1,5 @@ | |||
package com.jd.blockchain.contract; | |||
import com.jd.blockchain.ledger.ContractBizContent; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.KVDataObject; | |||
import com.jd.blockchain.utils.Bytes; | |||
@@ -34,11 +34,11 @@ | |||
<plugin> | |||
<artifactId>maven-assembly-plugin</artifactId> | |||
<configuration> | |||
<finalName>contract</finalName> | |||
<finalName>transfer</finalName> | |||
<appendAssemblyId>false</appendAssemblyId> | |||
<archive> | |||
<manifest> | |||
<mainClass>com.jd.blockchain.contract.ReadContractImpl</mainClass> | |||
<mainClass>com.jd.blockchain.contract.TransferContractImpl</mainClass> | |||
</manifest> | |||
</archive> | |||
<descriptorRefs> | |||
@@ -6,5 +6,10 @@ public interface ReadContract { | |||
@ContractEvent(name = "read-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; | |||
@Contract | |||
public class ReadContractImpl implements EventProcessingAwire, ReadContract { | |||
public class ReadContractImpl implements EventProcessingAware, ReadContract { | |||
private ContractEventContext eventContext; | |||
private HashDigest ledgerHash; | |||
@Override | |||
public void beforeEvent(ContractEventContext eventContext) { | |||
this.eventContext = eventContext; | |||
this.ledgerHash = eventContext.getCurrentLedgerHash(); | |||
} | |||
@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 | |||
@ContractEvent(name = "read-key") | |||
public String read(String address, String key) { | |||
HashDigest ledgerHash = eventContext.getCurrentLedgerHash(); | |||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | |||
if (kvDataEntries != null && kvDataEntries.length == 1) { | |||
@@ -40,4 +31,20 @@ public class ReadContractImpl implements EventProcessingAwire, ReadContract { | |||
} | |||
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, | |||
(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", | |||
(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> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>browser</artifactId> | |||
<artifactId>explorer</artifactId> | |||
</dependency> | |||
<dependency> | |||
@@ -78,6 +78,23 @@ | |||
<version>${commons-io.version}</version> | |||
</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> | |||
<groupId>org.springframework.boot</groupId> | |||
<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.ledger.ParticipantNode; | |||
import com.jd.blockchain.sdk.ContractSettings; | |||
import com.jd.blockchain.sdk.LedgerInitSettings; | |||
import com.jd.blockchain.utils.Bytes; | |||
/** | |||
* queryService only for gateway; | |||
@@ -34,4 +36,15 @@ public interface GatewayQueryService { | |||
* @return | |||
*/ | |||
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.crypto.HashDigest; | |||
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.ParticipantNode; | |||
import com.jd.blockchain.sdk.ContractSettings; | |||
import com.jd.blockchain.sdk.LedgerInitSettings; | |||
import com.jd.blockchain.utils.QueryUtil; | |||
import com.jd.blockchain.utils.codec.HexUtils; | |||
@@ -53,6 +56,21 @@ public class GatewayQueryServiceHandler implements GatewayQueryService { | |||
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.ledger.*; | |||
import com.jd.blockchain.sdk.BlockchainExtendQueryService; | |||
import com.jd.blockchain.sdk.ContractSettings; | |||
import com.jd.blockchain.sdk.LedgerInitSettings; | |||
import com.jd.blockchain.tools.keygen.KeyGenCommand; | |||
import com.jd.blockchain.utils.BaseConstant; | |||
@@ -23,6 +24,7 @@ import java.util.List; | |||
@RestController | |||
@RequestMapping(path = "/") | |||
public class BlockBrowserController implements BlockchainExtendQueryService { | |||
private static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(BlockBrowserController.class); | |||
@Autowired | |||
@@ -55,7 +57,7 @@ public class BlockBrowserController implements BlockchainExtendQueryService { | |||
// @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants") | |||
@Override | |||
public ParticipantNode[] getConsensusParticipants(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { | |||
public ParticipantNode[] getConsensusParticipants(HashDigest 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}") | |||
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 | |||
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") | |||
@@ -450,7 +457,8 @@ public class BlockBrowserController implements BlockchainExtendQueryService { | |||
} | |||
@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; | |||
if (dataRetrievalUrl == null || dataRetrievalUrl.length() <= 0) { | |||
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 | |||
*/ | |||
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.ledger.AccountHeader; | |||
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; | |||
public class ContractAccount implements AccountHeader { | |||
public class ContractAccount implements ContractInfo { | |||
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) { | |||
BytesValue bytesValue = BytesValueEntry.fromBytes(chaincode); | |||
BytesValue bytesValue = BytesData.fromBytes(chaincode); | |||
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) { | |||
BytesValue bytesValue = BytesValueEntry.fromText(value); | |||
BytesValue bytesValue = BytesData.fromText(value); | |||
return accBase.setBytes(encodePropertyKey(key), bytesValue, version); | |||
} | |||
public String getProperty(Bytes key) { | |||
BytesValue bytesValue = accBase.getBytes(encodePropertyKey(key)); | |||
return BytesValueEntry.toText(bytesValue); | |||
return BytesData.toText(bytesValue); | |||
} | |||
public String getProperty(Bytes key, long version) { | |||
BytesValue bytesValue = accBase.getBytes(encodePropertyKey(key), version); | |||
return BytesValueEntry.toText(bytesValue); | |||
return BytesData.toText(bytesValue); | |||
} | |||
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.ledger.AccountHeader; | |||
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.KVDataObject; | |||
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) { | |||
BytesValue bytesValue = BytesValueEntry.fromText(value); | |||
BytesValue bytesValue = BytesData.fromText(value); | |||
return baseAccount.setBytes(key, bytesValue, 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 the latest version entry associated the specified key; If the key | |||
* doesn't exist, then return -1; | |||
@@ -1,8 +1,8 @@ | |||
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 | |||
* | |||
* @return | |||
*/ | |||
LedgerTransaction commit(TransactionState txResult); | |||
/** | |||
* 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | |||
* 提交对账本数据的修改,以指定的交易状态提交交易; | |||
* | |||
* @param txResult | |||
* @param operationResults | |||
* | |||
* @return | |||
*/ | |||
LedgerTransaction commit(TransactionState txResult, List<OperationResult> operationResults); | |||
/** | |||
* 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | |||
* | |||
* 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||
* | |||
* | |||
* @param txResult | |||
* @return | |||
*/ | |||
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; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.Operation; | |||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | |||
public interface OperationHandle { | |||
/** | |||
@@ -14,8 +16,9 @@ public interface OperationHandle { | |||
boolean support(Class<?> operationType); | |||
/** | |||
* 解析和执行操作; | |||
* | |||
* 同步解析和执行操作; | |||
* | |||
* | |||
* @param op | |||
* 操作实例; | |||
* @param newBlockDataset | |||
@@ -24,8 +27,28 @@ public interface OperationHandle { | |||
* 交易请求上下文; | |||
* @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); | |||
// /** | |||
// * 异步解析和执行操作; | |||
// * 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.PubKey; | |||
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.utils.Bytes; | |||
@@ -50,12 +50,12 @@ public class UserAccount implements UserInfo { | |||
public long setDataPubKey(PubKey pubKey) { | |||
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) { | |||
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) { | |||
@@ -63,7 +63,7 @@ public class UserAccount implements UserInfo { | |||
} | |||
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) { | |||
@@ -3,16 +3,12 @@ package com.jd.blockchain.ledger.core.impl; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import javax.annotation.PostConstruct; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Component; | |||
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.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.DataAccountRegisterOperationHandle; | |||
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 { | |||
private List<OperationHandle> opHandles = new ArrayList<>(); | |||
// private UserRegisterOperationHandle userRegHandle; | |||
// | |||
// private DataAccountRegisterOperationHandle dataAccRegHandle; | |||
// | |||
// private DataAccountKVSetOperationHandle dataAccKVSetHandle; | |||
// | |||
// private ContractCodeDeployOperationHandle contractDplHandle; | |||
// | |||
// private ContractEventSendOperationHandle contractEvtSendHandle; | |||
public DefaultOperationHandleRegisteration() { | |||
initDefaultHandles(); | |||
} | |||
@@ -40,25 +25,30 @@ public class DefaultOperationHandleRegisteration implements OperationHandleRegis | |||
/** | |||
* 针对不采用bean依赖注入的方式来处理; | |||
*/ | |||
private void initDefaultHandles(){ | |||
private void initDefaultHandles() { | |||
opHandles.add(new DataAccountKVSetOperationHandle()); | |||
opHandles.add(new DataAccountRegisterOperationHandle()); | |||
opHandles.add(new UserRegisterOperationHandle()); | |||
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 | |||
public OperationHandle getHandle(Class<?> operationType) { | |||
@@ -69,6 +59,5 @@ public class DefaultOperationHandleRegisteration implements OperationHandleRegis | |||
} | |||
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.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.DataAccount; | |||
import com.jd.blockchain.ledger.core.DataAccountSet; | |||
@@ -276,15 +264,15 @@ public class LedgerQueryService implements BlockchainQueryService { | |||
KVDataEntry[] entries = new KVDataEntry[keys.length]; | |||
long ver; | |||
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) { | |||
entries[i] = new KVDataObject(keys[i], -1, null); | |||
entries[i] = new KVDataObject(currKey, -1, null); | |||
} 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 | |||
public AccountHeader getContract(HashDigest ledgerHash, String address) { | |||
public ContractInfo getContract(HashDigest ledgerHash, String address) { | |||
LedgerRepository ledger = ledgerService.getLedger(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); | |||
@@ -1,8 +1,12 @@ | |||
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.ledger.DigitalSignature; | |||
import com.jd.blockchain.ledger.LedgerTransaction; | |||
import com.jd.blockchain.ledger.OperationResult; | |||
import com.jd.blockchain.ledger.TransactionContent; | |||
import com.jd.blockchain.ledger.TransactionRequest; | |||
import com.jd.blockchain.ledger.TransactionState; | |||
@@ -23,6 +27,8 @@ public class LedgerTransactionData implements LedgerTransaction { | |||
private long blockHeight; | |||
private OperationResult[] operationResults; | |||
// private HashDigest adminAccountHash; | |||
// | |||
// private HashDigest userAccountSetHash; | |||
@@ -49,7 +55,7 @@ public class LedgerTransactionData implements LedgerTransaction { | |||
* 交易级的系统快照; | |||
*/ | |||
public LedgerTransactionData(long blockHeight, TransactionRequest txReq, TransactionState execState, | |||
TransactionStagedSnapshot txSnapshot) { | |||
TransactionStagedSnapshot txSnapshot, OperationResult... opResults) { | |||
this.blockHeight = blockHeight; | |||
// this.txSnapshot = txSnapshot == null ? new TransactionStagedSnapshot() : txSnapshot; | |||
this.txSnapshot = txSnapshot; | |||
@@ -57,6 +63,15 @@ public class LedgerTransactionData implements LedgerTransaction { | |||
this.endpointSignatures = txReq.getEndpointSignatures(); | |||
this.nodeSignatures = txReq.getNodeSignatures(); | |||
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 | |||
@@ -74,6 +89,11 @@ public class LedgerTransactionData implements LedgerTransaction { | |||
return executionState; | |||
} | |||
@Override | |||
public OperationResult[] getOperationResults() { | |||
return operationResults; | |||
} | |||
@Override | |||
public TransactionContent getTransactionContent() { | |||
return this.transactionContent; | |||
@@ -1,5 +1,6 @@ | |||
package com.jd.blockchain.ledger.core.impl; | |||
import java.util.List; | |||
import java.util.Stack; | |||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
@@ -351,6 +352,11 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||
@Override | |||
public LedgerTransaction commit(TransactionState txResult) { | |||
return commit(txResult, null); | |||
} | |||
@Override | |||
public LedgerTransaction commit(TransactionState txResult, List<OperationResult> operationResults) { | |||
checkTxState(); | |||
// capture snapshot | |||
@@ -359,7 +365,8 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||
// LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | |||
// 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.commit(); | |||
@@ -377,6 +384,11 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||
@Override | |||
public LedgerTransaction discardAndCommit(TransactionState txResult) { | |||
return discardAndCommit(txResult, null); | |||
} | |||
@Override | |||
public LedgerTransaction discardAndCommit(TransactionState txResult, List<OperationResult> operationResults) { | |||
checkTxState(); | |||
// 未处理 | |||
@@ -385,7 +397,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||
// TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); | |||
// LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | |||
// 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.commit(); | |||
@@ -410,6 +422,15 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||
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 | |||
public void rollback() { | |||
if (this.rollbacked) { | |||
@@ -3,16 +3,17 @@ package com.jd.blockchain.ledger.core.impl; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
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.LoggerFactory; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.LedgerBlock; | |||
import com.jd.blockchain.ledger.LedgerException; | |||
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.TransactionResponse; | |||
import com.jd.blockchain.ledger.TransactionState; | |||
@@ -30,7 +31,7 @@ import com.jd.blockchain.utils.Bytes; | |||
public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
private static final Logger LOGGER = LoggerFactory.getLogger(TransactionBatchProcessor.class); | |||
private LedgerService ledgerService; | |||
private LedgerEditor newBlockEditor; | |||
@@ -76,8 +77,11 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
// 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | |||
LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | |||
TransactionState result; | |||
List<CompletableFuture<String>> contractReturn = new ArrayList<>(); | |||
List<OperationResult> operationResults = new ArrayList<>(); | |||
try { | |||
LedgerDataSet dataset = txCtx.getDataSet(); | |||
TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | |||
// TODO: 验证签名者的有效性; | |||
@@ -103,46 +107,37 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
} | |||
}; | |||
OperationHandle opHandle; | |||
int opIndex = 0; | |||
for (Operation op : ops) { | |||
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; | |||
txCtx.commit(result); | |||
txCtx.commit(result, operationResults); | |||
} catch (LedgerException e) { | |||
// TODO: 识别更详细的异常类型以及执行对应的处理; | |||
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", | |||
request.getHash().toBase58(), e.getMessage()), e); | |||
} catch (Exception e) { | |||
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", | |||
request.getHash().toBase58(), e.getMessage()), e); | |||
} | |||
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); | |||
@@ -224,7 +219,7 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
private TransactionState result; | |||
private String[] contractReturn; | |||
private OperationResult[] operationResults; | |||
public TxResponseHandle(TransactionRequest request, TransactionState result) { | |||
this.request = request; | |||
@@ -257,12 +252,12 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
} | |||
@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; | |||
import static com.jd.blockchain.utils.BaseConstant.CONTRACT_SERVICE_PROVIDER; | |||
import org.springframework.stereotype.Service; | |||
import com.jd.blockchain.contract.LocalContractEventContext; | |||
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.LedgerException; | |||
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.OperationHandleContext; | |||
import java.util.concurrent.CompletableFuture; | |||
import java.util.concurrent.Future; | |||
@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 | |||
public boolean support(Class<?> 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; | |||
// 先从账本校验合约的有效性; | |||
// 注意:必须在前一个区块的数据集中进行校验,因为那是经过共识的数据;从当前新区块链数据集校验则会带来攻击风险:未经共识的合约得到执行; | |||
@@ -73,16 +54,15 @@ public class ContractEventSendOperationHandle implements OperationHandle { | |||
localContractEventContext.setArgs(contractOP.getArgs()).setTransactionRequest(requestContext.getRequest()) | |||
.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; | |||
import org.springframework.stereotype.Service; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.ContractCodeDeployOperation; | |||
import com.jd.blockchain.ledger.Operation; | |||
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.impl.OperationHandleContext; | |||
import org.springframework.stereotype.Service; | |||
@Service | |||
public class ContractCodeDeployOperationHandle implements OperationHandle { | |||
@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) { | |||
ContractCodeDeployOperation contractOP = (ContractCodeDeployOperation) op; | |||
// TODO: 校验合约代码的正确性; | |||
// TODO: 请求者应该提供合约账户的公钥签名,已确定注册的地址的唯一性; | |||
dataset.getContractAccountSet().deploy(contractOP.getContractID().getAddress(), | |||
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 | |||
public boolean support(Class<?> operationType) { | |||
return ContractCodeDeployOperation.class.isAssignableFrom(operationType); | |||
@@ -5,23 +5,7 @@ import java.util.List; | |||
import com.jd.blockchain.contract.LedgerContext; | |||
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.transaction.BlockchainQueryService; | |||
import com.jd.blockchain.transaction.DataAccountKVSetOperationBuilder; | |||
@@ -186,7 +170,7 @@ public class ContractLedgerContext implements LedgerContext { | |||
} | |||
@Override | |||
public AccountHeader getContract(HashDigest ledgerHash, String address) { | |||
public ContractInfo getContract(HashDigest ledgerHash, String address) { | |||
return innerQueryService.getContract(ledgerHash, address); | |||
} | |||
@@ -279,7 +263,7 @@ public class ContractLedgerContext implements LedgerContext { | |||
@Override | |||
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); | |||
handle(op); | |||
return this; | |||
@@ -287,7 +271,7 @@ public class ContractLedgerContext implements LedgerContext { | |||
@Override | |||
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); | |||
handle(op); | |||
return this; | |||
@@ -295,7 +279,7 @@ public class ContractLedgerContext implements LedgerContext { | |||
@Override | |||
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); | |||
handle(op); | |||
return this; | |||
@@ -312,7 +296,7 @@ public class ContractLedgerContext implements LedgerContext { | |||
@Override | |||
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); | |||
handle(op); | |||
return this; | |||
@@ -320,7 +304,7 @@ public class ContractLedgerContext implements LedgerContext { | |||
@Override | |||
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); | |||
handle(op); | |||
return this; | |||
@@ -328,7 +312,7 @@ public class ContractLedgerContext implements LedgerContext { | |||
@Override | |||
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); | |||
handle(op); | |||
return this; | |||
@@ -336,7 +320,7 @@ public class ContractLedgerContext implements LedgerContext { | |||
@Override | |||
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); | |||
handle(op); | |||
return this; | |||
@@ -344,7 +328,7 @@ public class ContractLedgerContext implements LedgerContext { | |||
@Override | |||
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); | |||
handle(op); | |||
return this; | |||
@@ -1,33 +1,44 @@ | |||
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.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.DataAccountKVSetOperation; | |||
import com.jd.blockchain.ledger.DataAccountKVSetOperation.KVWriteEntry; | |||
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.utils.Bytes; | |||
import org.springframework.stereotype.Service; | |||
@Service | |||
public class DataAccountKVSetOperationHandle implements OperationHandle{ | |||
public class DataAccountKVSetOperationHandle implements OperationHandle { | |||
static { | |||
DataContractRegistry.register(BytesValue.class); | |||
} | |||
@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) { | |||
DataAccountKVSetOperation kvWriteOp = (DataAccountKVSetOperation) op; | |||
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()); | |||
} | |||
return null; | |||
} | |||
// @Override | |||
// public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||
// return null; | |||
// } | |||
@Override | |||
public boolean support(Class<?> operationType) { | |||
return DataAccountKVSetOperation.class.isAssignableFrom(operationType); | |||
@@ -1,6 +1,9 @@ | |||
package com.jd.blockchain.ledger.core.impl.handles; | |||
import org.springframework.stereotype.Service; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.DataAccountRegisterOperation; | |||
import com.jd.blockchain.ledger.Operation; | |||
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.impl.OperationHandleContext; | |||
import org.springframework.stereotype.Service; | |||
@Service | |||
public class DataAccountRegisterOperationHandle implements OperationHandle{ | |||
public class DataAccountRegisterOperationHandle implements OperationHandle { | |||
@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) { | |||
DataAccountRegisterOperation dataAccountRegOp = (DataAccountRegisterOperation) op; | |||
BlockchainIdentity bid = dataAccountRegOp.getAccountID(); | |||
//TODO: 校验用户身份; | |||
//TODO: 请求者应该提供数据账户的公钥签名,已确定注册的地址的唯一性; | |||
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 | |||
public boolean support(Class<?> 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; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.Operation; | |||
import com.jd.blockchain.ledger.UserRegisterOperation; | |||
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.TransactionRequestContext; | |||
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 | |||
public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||
public BytesValue process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||
LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||
UserRegisterOperation userRegOp = (UserRegisterOperation) op; | |||
BlockchainIdentity bid = userRegOp.getUserID(); | |||
dataset.getUserAccountSet().register(bid.getAddress(), bid.getPubKey()); | |||
Bytes userAddress = bid.getAddress(); | |||
dataset.getUserAccountSet().register(userAddress, bid.getPubKey()); | |||
return null; | |||
} | |||
@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.ledger.BlockchainKeyGenerator; | |||
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.CryptoConfig; | |||
import com.jd.blockchain.storage.service.utils.MemoryKVStorage; | |||
@@ -53,33 +53,33 @@ public class BaseAccountTest { | |||
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); | |||
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); | |||
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); | |||
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,预期导致失败; | |||
assertEquals(-1, v); | |||
baseAccount.commit(); | |||
v = 0; | |||
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(); | |||
// 预期成功; | |||
assertEquals(v + 1, s); | |||
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); | |||
@@ -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.ledger.BlockchainKeypair; | |||
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.LedgerInitSetting; | |||
import com.jd.blockchain.ledger.LedgerTransaction; | |||
@@ -68,17 +68,17 @@ public class LedgerEditerTest { | |||
MemoryKVStorage storage = new MemoryKVStorage(); | |||
// 创建初始化配置; | |||
LedgerInitSetting initSetting = createLedgerInitSetting(); | |||
LedgerInitSetting initSetting = LedgerTestUtils.createLedgerInitSetting(); | |||
// 创建账本; | |||
return LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); | |||
} | |||
private LedgerTransactionContext createGenisisTx(LedgerEditor ldgEdt) { | |||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest(null, signatureFunction); | |||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||
LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | |||
return txCtx; | |||
} | |||
@@ -88,8 +88,7 @@ public class LedgerEditerTest { | |||
LedgerEditor ldgEdt = createLedgerInitEditor(); | |||
LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt); | |||
LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); | |||
AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | |||
BlockchainKeypair dataKP = new BlockchainKeypair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); | |||
@@ -109,7 +108,7 @@ public class LedgerEditerTest { | |||
// 验证数据读写的一致性; | |||
BytesValue bytes = dataAccount.getBytes("A"); | |||
assertEquals(BytesValueType.TEXT, bytes.getType()); | |||
assertEquals(DataType.TEXT, bytes.getType()); | |||
String textValue = bytes.getValue().toUTF8String(); | |||
assertEquals("abc", textValue); | |||
} | |||
@@ -147,42 +146,5 @@ public class LedgerEditerTest { | |||
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); | |||
// 创建一个模拟的创世交易; | |||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest(null, signatureFunction); | |||
TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); | |||
// 记录交易,注册用户; | |||
LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); | |||
LedgerDataSet ldgDS = txCtx.getDataSet(); | |||
BlockchainKeypair userKP = BlockchainKeyGenerator.getInstance().generate(); | |||
; | |||
UserAccount userAccount = ldgDS.getUserAccountSet().register(userKP.getAddress(), userKP.getPubKey()); | |||
userAccount.setProperty("Name", "孙悟空", -1); | |||
userAccount.setProperty("Age", "10000", -1); | |||
@@ -2,69 +2,103 @@ package test.com.jd.blockchain.ledger; | |||
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.CryptoAlgorithm; | |||
import com.jd.blockchain.crypto.CryptoProvider; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.crypto.SignatureFunction; | |||
import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; | |||
import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | |||
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.PreparedTransaction; | |||
import com.jd.blockchain.ledger.LedgerInitSetting; | |||
import com.jd.blockchain.ledger.TransactionRequest; | |||
import com.jd.blockchain.ledger.TransactionRequestBuilder; | |||
import com.jd.blockchain.ledger.TransactionResponse; | |||
import com.jd.blockchain.ledger.core.CryptoConfig; | |||
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.TxTemplate; | |||
import com.jd.blockchain.transaction.TxBuilder; | |||
import com.jd.blockchain.utils.io.BytesUtils; | |||
import com.jd.blockchain.utils.net.NetworkAddress; | |||
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(), | |||
SMCryptoService.class.getName() }; | |||
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() { | |||
TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); | |||
@@ -68,7 +68,7 @@ public class LedgerTransactionDataTest { | |||
long blockHeight = 9986L; | |||
data = new LedgerTransactionData(blockHeight, txRequestMessage, TransactionState.SUCCESS, | |||
initTransactionStagedSnapshot()); | |||
initTransactionStagedSnapshot(), null); | |||
HashDigest hash = new HashDigest(ClassicAlgorithm.SHA256, "zhangsan".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; | |||
import static org.junit.Assert.assertArrayEquals; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertNotNull; | |||
@@ -10,10 +11,13 @@ import java.util.Random; | |||
import org.junit.Test; | |||
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.BytesDataList; | |||
import com.jd.blockchain.ledger.BytesValueList; | |||
import com.jd.blockchain.ledger.ContractCodeDeployOperation; | |||
import com.jd.blockchain.ledger.ContractEventSendOperation; | |||
import com.jd.blockchain.ledger.CryptoSetting; | |||
@@ -67,8 +71,8 @@ public class TransactionSetTest { | |||
BlockchainKeypair dataKey = BlockchainKeyGenerator.getInstance().generate(); | |||
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]; | |||
rand.nextBytes(chainCode); | |||
@@ -76,7 +80,7 @@ public class TransactionSetTest { | |||
ContractCodeDeployOperation contractDplOP = txBuilder.contracts().deploy(contractKey.getIdentity(), chainCode); | |||
ContractEventSendOperation contractEvtSendOP = txBuilder.contractEvents().send(contractKey.getAddress(), "test", | |||
"TestContractArgs".getBytes()); | |||
BytesDataList.singleText("TestContractArgs")); | |||
TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||
@@ -98,7 +102,8 @@ public class TransactionSetTest { | |||
txSnapshot.setContractAccountSetHash(contractAccountSetHash); | |||
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); | |||
assertTrue(txset.isUpdated()); | |||
@@ -172,7 +177,8 @@ public class TransactionSetTest { | |||
for (int i = 0; i < acutualKVWriteSet.length; i++) { | |||
assertEquals(expKVWriteSet[i].getKey(), acutualKVWriteSet[i].getKey()); | |||
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]; | |||
@@ -184,8 +190,14 @@ public class TransactionSetTest { | |||
assertEquals(contractEvtSendOP.getContractAddress(), actualContractEvtSendOp.getContractAddress()); | |||
assertEquals(contractEvtSendOP.getEvent(), 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> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
</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; | |||
import java.util.Set; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.BytesValueList; | |||
import com.jd.blockchain.ledger.TransactionRequest; | |||
import java.util.Set; | |||
public interface ContractEventContext { | |||
@@ -42,7 +43,7 @@ public interface ContractEventContext { | |||
* | |||
* @return | |||
*/ | |||
byte[] getArgs(); | |||
BytesValueList getArgs(); | |||
/** | |||
* 账本操作上下文; | |||
@@ -1,16 +1,17 @@ | |||
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) { | |||
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 | |||
* | |||
*/ | |||
public interface ContractAppLifecycleAwire extends ContractRuntimeAwire { | |||
public interface ContractLifecycleAware extends ContractAware { | |||
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 | |||
* | |||
*/ | |||
public interface EventProcessingAwire extends ContractRuntimeAwire { | |||
public interface EventProcessingAware extends ContractAware { | |||
/** | |||
* 在事件处理方法执行之前调用; | |||
@@ -21,19 +21,6 @@ public interface EventProcessingAwire extends ContractRuntimeAwire { | |||
* @param error | |||
* 错误;如果事件处理正常结束,则此参数为 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.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.BytesValueList; | |||
import com.jd.blockchain.ledger.TransactionRequest; | |||
/** | |||
@@ -13,17 +14,14 @@ import com.jd.blockchain.ledger.TransactionRequest; | |||
public class LocalContractEventContext implements ContractEventContext,Cloneable { | |||
private HashDigest ledgeHash; | |||
private String event; | |||
private byte[] chainCode; | |||
private byte[] args; | |||
private BytesValueList args; | |||
private TransactionRequest transactionRequest; | |||
private Set<BlockchainIdentity> txSigners; | |||
private Set<BlockchainIdentity> contractOwners; | |||
private LedgerContext ledgerContext; | |||
public LocalContractEventContext(HashDigest ledgeHash, String event){ | |||
this.ledgeHash = ledgeHash; | |||
this.event = event; | |||
// this.chainCode = chainCode; | |||
} | |||
@Override | |||
@@ -47,7 +45,7 @@ public class LocalContractEventContext implements ContractEventContext,Cloneable | |||
} | |||
@Override | |||
public byte[] getArgs() { | |||
public BytesValueList getArgs() { | |||
return args; | |||
} | |||
@@ -81,11 +79,6 @@ public class LocalContractEventContext implements ContractEventContext,Cloneable | |||
return this; | |||
} | |||
public LocalContractEventContext setContractOwners(Set<BlockchainIdentity> contractOwners) { | |||
this.contractOwners = contractOwners; | |||
return this; | |||
} | |||
public LocalContractEventContext setLedgerContext(LedgerContext ledgerContext) { | |||
this.ledgerContext = ledgerContext; | |||
return this; | |||
@@ -100,7 +93,7 @@ public class LocalContractEventContext implements ContractEventContext,Cloneable | |||
// return this; | |||
// } | |||
public LocalContractEventContext setArgs(byte[] args) { | |||
public LocalContractEventContext setArgs(BytesValueList args) { | |||
this.args = args; | |||
return this; | |||
} | |||
@@ -8,38 +8,38 @@ import com.jd.blockchain.utils.io.BytesUtils; | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class BytesValueEntry implements BytesValue { | |||
BytesValueType type; | |||
public class BytesData implements BytesValue { | |||
DataType type; | |||
Bytes value; | |||
private BytesValueEntry(BytesValueType type, byte[] bytes) { | |||
private BytesData(DataType type, byte[] bytes) { | |||
this.type = type; | |||
this.value = new Bytes(bytes); | |||
} | |||
private BytesValueEntry(BytesValueType type, Bytes bytes) { | |||
private BytesData(DataType type, Bytes bytes) { | |||
this.type = type; | |||
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) { | |||
return new BytesValueEntry(BytesValueType.BYTES, value); | |||
return new BytesData(DataType.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) { | |||
return new BytesValueEntry(BytesValueType.IMG, value); | |||
return new BytesData(DataType.IMG, 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 | |||
*/ | |||
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) { | |||
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() + "!"); | |||
} | |||
return bytesValue.getValue().toUTF8String(); | |||
} | |||
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) { | |||
return new BytesValueEntry(BytesValueType.XML, BytesUtils.toBytes(value)); | |||
return new BytesData(DataType.XML, BytesUtils.toBytes(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) { | |||
return new BytesValueEntry(BytesValueType.INT64, BytesUtils.toBytes(value)); | |||
return new BytesData(DataType.INT64, BytesUtils.toBytes(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) { | |||
return new BytesValueEntry(BytesValueType.INT8, BytesUtils.toBytes(value)); | |||
return new BytesData(DataType.INT8, BytesUtils.toBytes(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) { | |||
return new BytesValueEntry(BytesValueType.BOOLEAN, BytesUtils.toBytes(value)); | |||
return new BytesData(DataType.BOOLEAN, BytesUtils.toBytes(value)); | |||
} | |||
@Override | |||
public BytesValueType getType() { | |||
public DataType getType() { | |||
return this.type; | |||
} | |||
public void setType(BytesValueType type) { | |||
public void setType(DataType 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 | |||
*/ | |||
@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; | |||
/** | |||
* 发送合约事件的操作; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
@@ -34,15 +36,7 @@ public interface ContractEventSendOperation extends Operation { | |||
* | |||
* @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; | |||
@DataContract(code= DataCodes.TX_OP_DATA_ACC_SET) | |||
public interface DataAccountKVSetOperation extends Operation{ | |||
public interface DataAccountKVSetOperation extends Operation { | |||
@DataField(order=2, primitiveType=PrimitiveType.BYTES) | |||
Bytes getAccountAddress(); | |||
@@ -1,8 +1,9 @@ | |||
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.EnumField; | |||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||
import com.jd.blockchain.consts.DataCodes; | |||
/** | |||
@@ -11,8 +12,8 @@ import com.jd.blockchain.consts.DataCodes; | |||
* @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), | |||
/** | |||
* 时间戳; | |||
* 文本数据; | |||
*/ | |||
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) | |||
public final byte CODE; | |||
private BytesValueType(byte code) { | |||
private DataType(byte 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) { | |||
return dataType; | |||
} |
@@ -25,7 +25,7 @@ public interface KVDataEntry { | |||
* | |||
* @return | |||
*/ | |||
BytesValueType getType(); | |||
DataType getType(); | |||
/** | |||
* 值; | |||
@@ -58,8 +58,8 @@ public class KVDataObject implements KVDataEntry { | |||
* @see com.jd.blockchain.ledger.KVDataEntry#getType() | |||
*/ | |||
@Override | |||
public BytesValueType getType() { | |||
return bytesValue == null ? BytesValueType.NIL : bytesValue.getType(); | |||
public DataType getType() { | |||
return bytesValue == null ? DataType.NIL : bytesValue.getType(); | |||
} | |||
@Override | |||
@@ -95,7 +95,7 @@ public class KVDataObject implements KVDataEntry { | |||
* @return | |||
*/ | |||
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 | |||
*/ | |||
public byte tinyValue() { | |||
if (BytesValueType.INT8 == getType()) { | |||
if (DataType.INT8 == getType()) { | |||
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 | |||
*/ | |||
public short shortValue() { | |||
if (BytesValueType.INT16 == getType()) { | |||
if (DataType.INT16 == getType()) { | |||
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 | |||
*/ | |||
public int intValue() { | |||
if (BytesValueType.INT32 == getType()) { | |||
if (DataType.INT32 == getType()) { | |||
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 | |||
*/ | |||
public long longValue() { | |||
if (BytesValueType.INT64 == getType()) { | |||
if (DataType.INT64 == getType()) { | |||
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 | |||
*/ | |||
public BigInteger bigIntValue() { | |||
if (BytesValueType.BIG_INT == getType()) { | |||
if (DataType.BIG_INT == getType()) { | |||
return new BigInteger(bytesValue.getValue().toBytes()); | |||
} | |||
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 | |||
*/ | |||
public boolean boolValue() { | |||
if (BytesValueType.BOOLEAN == getType()) { | |||
if (DataType.BOOLEAN == getType()) { | |||
return BytesUtils.toBoolean(bytesValue.getValue().toBytes()[0]); | |||
} | |||
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 | |||
*/ | |||
public Date datetimeValue() { | |||
if (BytesValueType.TIMESTAMP == getType()) { | |||
if (DataType.TIMESTAMP == getType()) { | |||
long ts = BytesUtils.toLong(bytesValue.getValue().toBytes()); | |||
return new Date(ts); | |||
} | |||
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 | |||
*/ | |||
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(); | |||
} | |||
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 long[] version; | |||
public KVDataVO() { | |||
} | |||
public KVDataVO(String key, long[] version) { | |||
this.key = key; | |||
this.version = version; | |||
} | |||
public String getKey() { | |||
return key; | |||
} | |||
@@ -8,6 +8,13 @@ package com.jd.blockchain.ledger; | |||
public class KVInfoVO { | |||
private KVDataVO[] data; | |||
public KVInfoVO() { | |||
} | |||
public KVInfoVO(KVDataVO[] data) { | |||
this.data = data; | |||
} | |||
public KVDataVO[] getData() { | |||
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; | |||
import java.io.Closeable; | |||
import com.jd.blockchain.crypto.AsymmetricKeypair; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.crypto.SignatureFunction; | |||
/** | |||
* 已就绪的交易; | |||
@@ -10,7 +11,7 @@ import com.jd.blockchain.crypto.SignatureFunction; | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public interface PreparedTransaction extends HashObject { | |||
public interface PreparedTransaction extends HashObject, Closeable { | |||
/** | |||
* 交易内容的 Hash; | |||
@@ -33,10 +34,8 @@ public interface PreparedTransaction extends HashObject { | |||
/** | |||
* 对交易进行签名; | |||
* | |||
* @param address | |||
* 签名账户的地址; | |||
* @param privKey | |||
* 签名账户的私钥; | |||
* @param address 签名账户的地址; | |||
* @param privKey 签名账户的私钥; | |||
* @return | |||
*/ | |||
DigitalSignature sign(AsymmetricKeypair keyPair); | |||
@@ -44,17 +43,18 @@ public interface PreparedTransaction extends HashObject { | |||
/** | |||
* 加入签名; | |||
* | |||
* @param address | |||
* 签名账户的地址; | |||
* @param digest | |||
* Base64格式的签名摘要; | |||
* @param address 签名账户的地址; | |||
* @param digest Base64格式的签名摘要; | |||
* @return | |||
*/ | |||
void addSignature(DigitalSignature signature); | |||
/** | |||
* 生成交易请求; | |||
* 提交交易请求到共识节点;<br> | |||
* | |||
* 这是同步方法,将阻塞当前线程,直到交易处理完成并返回结果之后,此方法才返回给调用者; | |||
* | |||
*/ | |||
TransactionResponse commit(); | |||
} |
@@ -5,7 +5,6 @@ import com.jd.blockchain.binaryproto.DataField; | |||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||
import com.jd.blockchain.consts.DataCodes; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.utils.io.ByteArray; | |||
/** | |||
* Transaction 区块链交易,是被原子执行的操作集合; | |||
@@ -13,7 +12,7 @@ import com.jd.blockchain.utils.io.ByteArray; | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
@DataContract(code= DataCodes.TX) | |||
@DataContract(code = DataCodes.TX) | |||
public interface Transaction extends NodeRequest, HashObject { | |||
/** | |||
@@ -23,7 +22,7 @@ public interface Transaction extends NodeRequest, HashObject { | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=1, primitiveType = PrimitiveType.BYTES) | |||
@DataField(order = 1, primitiveType = PrimitiveType.BYTES) | |||
@Override | |||
HashDigest getHash(); | |||
@@ -32,7 +31,7 @@ public interface Transaction extends NodeRequest, HashObject { | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=2, primitiveType=PrimitiveType.INT64) | |||
@DataField(order = 2, primitiveType = PrimitiveType.INT64) | |||
long getBlockHeight(); | |||
/** | |||
@@ -42,7 +41,14 @@ public interface Transaction extends NodeRequest, HashObject { | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=3, refEnum=true) | |||
@DataField(order = 3, refEnum = true) | |||
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 String[] contractReturn; | |||
private OperationResult[] operationResults; | |||
public TransactionRespHandle(TransactionRequest request, TransactionState result, TransactionState globalResult) { | |||
this.request = request; | |||
@@ -51,8 +51,8 @@ public class TransactionRespHandle implements TransactionResponse { | |||
this.result = result; | |||
} | |||
public void setContractReturn(String[] contractReturn) { | |||
this.contractReturn = contractReturn; | |||
public void setOperationResults(OperationResult[] operationResults) { | |||
this.operationResults = operationResults; | |||
} | |||
public LedgerBlock getBlock() { | |||
@@ -97,7 +97,7 @@ public class TransactionRespHandle implements TransactionResponse { | |||
} | |||
@Override | |||
public String[] getContractReturn() { | |||
return contractReturn; | |||
public OperationResult[] getOperationResults() { | |||
return operationResults; | |||
} | |||
} |
@@ -12,7 +12,7 @@ import com.jd.blockchain.crypto.HashDigest; | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
@DataContract(code= DataCodes.TX_RESPONSE) | |||
@DataContract(code = DataCodes.TX_RESPONSE) | |||
public interface TransactionResponse { | |||
/** | |||
@@ -20,7 +20,7 @@ public interface TransactionResponse { | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=1, primitiveType = PrimitiveType.BYTES) | |||
@DataField(order = 1, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getContentHash(); | |||
/** | |||
@@ -28,7 +28,7 @@ public interface TransactionResponse { | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=2, refEnum=true) | |||
@DataField(order = 2, refEnum = true) | |||
TransactionState getExecutionState(); | |||
/** | |||
@@ -36,7 +36,7 @@ public interface TransactionResponse { | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=3, primitiveType = PrimitiveType.BYTES) | |||
@DataField(order = 3, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getBlockHash(); | |||
/** | |||
@@ -47,7 +47,7 @@ public interface TransactionResponse { | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=4, primitiveType=PrimitiveType.INT64) | |||
@DataField(order = 4, primitiveType = PrimitiveType.INT64) | |||
long getBlockHeight(); | |||
/** | |||
@@ -55,7 +55,7 @@ public interface TransactionResponse { | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=5, primitiveType=PrimitiveType.BOOLEAN) | |||
@DataField(order = 5, primitiveType = PrimitiveType.BOOLEAN) | |||
boolean isSuccess(); | |||
/** | |||
@@ -63,6 +63,6 @@ public interface TransactionResponse { | |||
* | |||
* @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; | |||
import java.io.Closeable; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.transaction.ClientOperator; | |||
@@ -9,7 +11,7 @@ import com.jd.blockchain.transaction.ClientOperator; | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public interface TransactionTemplate extends ClientOperator { | |||
public interface TransactionTemplate extends ClientOperator, Closeable { | |||
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; | |||
} | |||
} |