| @@ -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; | |||
| @@ -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(); | |||
| byte[] processEvent(ContractEventContext eventContext); | |||
| BytesValue processEvent(ContractEventContext eventContext); | |||
| } | |||
| @@ -10,6 +10,7 @@ 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.utils.Bytes; | |||
| /** | |||
| @@ -44,7 +45,7 @@ public abstract class AbstractContractCode implements ContractCode { | |||
| } | |||
| @Override | |||
| public byte[] processEvent(ContractEventContext eventContext) { | |||
| public BytesValue processEvent(ContractEventContext eventContext) { | |||
| EventProcessingAware evtProcAwire = null; | |||
| Object retn = null; | |||
| Exception error = null; | |||
| @@ -90,13 +91,13 @@ public abstract class AbstractContractCode implements ContractCode { | |||
| eventContext.getEvent(), address.toString(), error.getMessage()), error); | |||
| } | |||
| byte[] retnBytes = resolveResult(retn); | |||
| BytesValue retnBytes = resolveResult(retn); | |||
| return retnBytes; | |||
| } | |||
| protected abstract Object getContractInstance(); | |||
| private byte[] resolveResult(Object retn) { | |||
| private BytesValue resolveResult(Object retn) { | |||
| if (retn == null) { | |||
| return null; | |||
| } | |||
| @@ -9,6 +9,7 @@ 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; | |||
| @@ -65,7 +66,7 @@ public class JavaContractCode extends AbstractContractCode { | |||
| } | |||
| @Override | |||
| public byte[] processEvent(ContractEventContext eventContext) { | |||
| public BytesValue processEvent(ContractEventContext eventContext) { | |||
| if (LOGGER.isDebugEnabled()) { | |||
| LOGGER.debug("Start processing event[%s] of contract[%s]...", eventContext.getEvent(), address.toString()); | |||
| } | |||
| @@ -91,7 +92,7 @@ public class JavaContractCode extends AbstractContractCode { | |||
| } | |||
| } | |||
| private class ContractExecution implements Callable<byte[]> { | |||
| private class ContractExecution implements Callable<BytesValue> { | |||
| private ContractEventContext eventContext; | |||
| public ContractExecution(ContractEventContext contractEventContext) { | |||
| @@ -99,7 +100,7 @@ public class JavaContractCode extends AbstractContractCode { | |||
| } | |||
| @Override | |||
| public byte[] call() throws Exception { | |||
| public BytesValue call() throws Exception { | |||
| return JavaContractCode.super.processEvent(eventContext); | |||
| } | |||
| } | |||
| @@ -1,8 +1,8 @@ | |||
| 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; | |||
| import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||
| public interface OperationHandle { | |||
| @@ -30,7 +30,7 @@ public interface OperationHandle { | |||
| * | |||
| * @return 操作执行结果 | |||
| */ | |||
| byte[] process(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, | |||
| BytesValue process(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, | |||
| LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService); | |||
| // /** | |||
| @@ -8,7 +8,7 @@ import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import com.jd.blockchain.crypto.HashDigest; | |||
| import com.jd.blockchain.ledger.ContractEventSendOperation; | |||
| import com.jd.blockchain.ledger.BytesValue; | |||
| import com.jd.blockchain.ledger.LedgerBlock; | |||
| import com.jd.blockchain.ledger.LedgerException; | |||
| import com.jd.blockchain.ledger.Operation; | |||
| @@ -110,7 +110,7 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
| int opIndex = 0; | |||
| for (Operation op : ops) { | |||
| opHandle = opHandles.getHandle(op.getClass()); | |||
| byte[] opResult = 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)); | |||
| } | |||
| @@ -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; | |||
| @@ -29,7 +26,7 @@ public abstract class AbtractContractEventHandle implements OperationHandle { | |||
| } | |||
| @Override | |||
| public byte[] process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||
| public BytesValue process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||
| LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { | |||
| ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | |||
| // 先从账本校验合约的有效性; | |||
| @@ -1,6 +1,8 @@ | |||
| package com.jd.blockchain.ledger.core.impl.handles; | |||
| import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
| 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; | |||
| @@ -9,17 +11,11 @@ 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 com.jd.blockchain.utils.concurrent.AsyncFuture; | |||
| import org.springframework.stereotype.Service; | |||
| import java.util.concurrent.Future; | |||
| @Service | |||
| public class ContractCodeDeployOperationHandle implements OperationHandle { | |||
| @Override | |||
| public byte[] 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: 校验合约代码的正确性; | |||
| @@ -1,7 +1,5 @@ | |||
| package com.jd.blockchain.ledger.core.impl.handles; | |||
| import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
| import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||
| import org.springframework.stereotype.Service; | |||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
| @@ -24,7 +22,7 @@ public class DataAccountKVSetOperationHandle implements OperationHandle { | |||
| } | |||
| @Override | |||
| public byte[] 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()); | |||
| @@ -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,14 +12,11 @@ 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.concurrent.AsyncFuture; | |||
| import org.springframework.stereotype.Service; | |||
| @Service | |||
| public class DataAccountRegisterOperationHandle implements OperationHandle { | |||
| @Override | |||
| public byte[] 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(); | |||
| @@ -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; | |||
| @@ -9,13 +10,12 @@ 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 com.jd.blockchain.utils.concurrent.AsyncFuture; | |||
| public class UserRegisterOperationHandle implements OperationHandle { | |||
| @Override | |||
| public byte[] process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||
| public BytesValue process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||
| LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||
| @@ -26,14 +26,9 @@ public class UserRegisterOperationHandle implements OperationHandle { | |||
| dataset.getUserAccountSet().register(userAddress, bid.getPubKey()); | |||
| return userAddress.toBytes(); | |||
| 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 UserRegisterOperation.class.isAssignableFrom(operationType); | |||
| @@ -120,8 +120,9 @@ public class ContractInvokingTest { | |||
| assertEquals(1, opResults.length); | |||
| assertEquals(0, opResults[0].getIndex()); | |||
| byte[] retnBytes = BinaryProtocol.encode(BytesValueEntry.fromInt64(issueAmount), BytesValue.class); | |||
| assertArrayEquals(retnBytes, opResults[0].getResult()); | |||
| byte[] expectedRetnBytes = BinaryProtocol.encode(BytesValueEntry.fromInt64(issueAmount), BytesValue.class); | |||
| byte[] reallyRetnBytes = BinaryProtocol.encode(opResults[0].getResult(), BytesValue.class); | |||
| assertArrayEquals(expectedRetnBytes, reallyRetnBytes); | |||
| // 提交区块; | |||
| TransactionBatchResultHandle txResultHandle = txbatchProcessor.prepare(); | |||
| @@ -135,7 +136,10 @@ public class ContractInvokingTest { | |||
| // 再验证一次结果; | |||
| assertEquals(1, opResults.length); | |||
| assertEquals(0, opResults[0].getIndex()); | |||
| assertArrayEquals(retnBytes, opResults[0].getResult()); | |||
| reallyRetnBytes = BinaryProtocol.encode(opResults[0].getResult(), BytesValue.class); | |||
| assertArrayEquals(expectedRetnBytes, reallyRetnBytes); | |||
| } | |||
| private HashDigest initLedger(MemoryKVStorage storage, BlockchainKeypair... partiKeys) { | |||
| @@ -1,157 +1,164 @@ | |||
| package com.jd.blockchain.contract; | |||
| import com.jd.blockchain.binaryproto.*; | |||
| import com.jd.blockchain.contract.param.*; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| 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.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 byte[] 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 byte[] 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(byte[] 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; | |||
| } | |||
| } | |||
| 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 byte[] 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; | |||
| } | |||
| } | |||
| } | |||
| @@ -63,24 +63,24 @@ public class ContractType { | |||
| /** | |||
| * 解析合约的声明; | |||
| * | |||
| * @param delaredInterface 声明合约的接口类型; | |||
| * @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) { | |||
| 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())); | |||
| throw new IllegalDataException(String | |||
| .format("%s is not a Contract Type, because there is not @Contract !", contractIntf.getName())); | |||
| } | |||
| contractIntf = realIntf; | |||
| } | |||
| @@ -105,22 +105,25 @@ public class ContractType { | |||
| 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)){ | |||
| // 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. | |||
| // check param's type is fit for need. | |||
| Class<?>[] paramTypes = method.getParameterTypes(); | |||
| for(Class<?> currParamType : paramTypes) { | |||
| for (Class<?> currParamType : paramTypes) { | |||
| if (!ContractSerializeUtils.support(currParamType)) { | |||
| throw new IllegalStateException(String.format("Param Type = %s can not support !!!", currParamType.getName())); | |||
| throw new IllegalStateException( | |||
| String.format("Param Type = %s can not support !!!", currParamType.getName())); | |||
| } | |||
| } | |||
| // 判断返回值是否可序列化 | |||
| Class<?> returnType = method.getReturnType(); | |||
| if (!ContractSerializeUtils.support(returnType)) { | |||
| throw new IllegalStateException(String.format("Return Type = %s can not support !!!", returnType.getName())); | |||
| throw new IllegalStateException( | |||
| String.format("Return Type = %s can not support !!!", returnType.getName())); | |||
| } | |||
| contractType.events.put(eventName, method); | |||
| @@ -132,10 +135,7 @@ public class ContractType { | |||
| @Override | |||
| public String toString() { | |||
| return "ContractType{" + | |||
| "name='" + name + '\'' + | |||
| ", events=" + events + | |||
| ", handleMethods=" + handleMethods + | |||
| '}'; | |||
| return "ContractType{" + "name='" + name + '\'' + ", events=" + events + ", handleMethods=" + handleMethods | |||
| + '}'; | |||
| } | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| package com.jd.blockchain.ledger; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.consts.DataCodes; | |||
| @DataContract(code = DataCodes.BYTES_VALUE_LIST) | |||
| public interface BytesValueList { | |||
| BytesValue[] getValues(); | |||
| } | |||
| @@ -37,11 +37,4 @@ public interface ContractEventSendOperation extends Operation { | |||
| @DataField(order = 4, primitiveType = PrimitiveType.BYTES) | |||
| byte[] getArgs(); | |||
| /** | |||
| * 获得交易操作时间; | |||
| * | |||
| * @return | |||
| */ | |||
| @DataField(order = 5, primitiveType = PrimitiveType.INT64) | |||
| long getTxOpTime(); | |||
| } | |||
| @@ -36,24 +36,22 @@ public enum DataType { | |||
| INT32(PrimitiveType.INT32.CODE), | |||
| INT64(PrimitiveType.INT64.CODE), | |||
| /** | |||
| * 文本数据; | |||
| */ | |||
| TEXT(PrimitiveType.TEXT.CODE), | |||
| /** | |||
| * 二进制数据; | |||
| */ | |||
| BYTES(PrimitiveType.BYTES.CODE), | |||
| /** | |||
| * 时间戳; | |||
| */ | |||
| TIMESTAMP((byte) (BaseType.INTEGER | 0x08)), | |||
| /** | |||
| * 文本数据; | |||
| */ | |||
| @@ -64,7 +62,6 @@ public enum DataType { | |||
| */ | |||
| XML((byte) (BaseType.TEXT | 0x02)), | |||
| /** | |||
| * 大整数; | |||
| */ | |||
| @@ -84,28 +81,32 @@ public enum DataType { | |||
| * 位置坐标; | |||
| */ | |||
| 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)); | |||
| ENCRYPTED_DATA((byte) (BaseType.BYTES | 0x08)), | |||
| /** | |||
| * DataContract 数据; | |||
| */ | |||
| DATA_CONTRACT((byte) (BaseType.EXT | 0x01)); | |||
| @EnumField(type = PrimitiveType.INT8) | |||
| public final byte CODE; | |||
| @@ -8,9 +8,9 @@ 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 = 1, primitiveType = PrimitiveType.INT32) | |||
| int getIndex(); | |||
| @DataField(order=2, primitiveType = PrimitiveType.BYTES) | |||
| byte[] getResult(); | |||
| @DataField(order = 2, refContract = true) | |||
| BytesValue getResult(); | |||
| } | |||
| @@ -5,12 +5,12 @@ public class OperationResultData implements OperationResult { | |||
| private int index; | |||
| private byte[] result; | |||
| private BytesValue result; | |||
| public OperationResultData() { | |||
| } | |||
| public OperationResultData(int index, byte[] result) { | |||
| public OperationResultData(int index, BytesValue result) { | |||
| this.index = index; | |||
| this.result = result; | |||
| } | |||
| @@ -21,7 +21,7 @@ public class OperationResultData implements OperationResult { | |||
| } | |||
| @Override | |||
| public byte[] getResult() { | |||
| public BytesValue getResult() { | |||
| return result; | |||
| } | |||
| @@ -29,7 +29,7 @@ public class OperationResultData implements OperationResult { | |||
| this.index = index; | |||
| } | |||
| public void setResult(byte[] result) { | |||
| public void setResult(BytesValue result) { | |||
| this.result = result; | |||
| } | |||
| } | |||
| @@ -2,7 +2,6 @@ package com.jd.blockchain.ledger; | |||
| import com.jd.blockchain.crypto.AsymmetricKeypair; | |||
| import com.jd.blockchain.crypto.HashDigest; | |||
| import com.jd.blockchain.crypto.SignatureFunction; | |||
| /** | |||
| * 已就绪的交易; | |||
| @@ -10,7 +9,7 @@ import com.jd.blockchain.crypto.SignatureFunction; | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| public interface PreparedTransaction extends HashObject { | |||
| public interface PreparedTransaction extends HashObject { | |||
| /** | |||
| * 交易内容的 Hash; | |||
| @@ -33,10 +32,8 @@ public interface PreparedTransaction extends HashObject { | |||
| /** | |||
| * 对交易进行签名; | |||
| * | |||
| * @param address | |||
| * 签名账户的地址; | |||
| * @param privKey | |||
| * 签名账户的私钥; | |||
| * @param address 签名账户的地址; | |||
| * @param privKey 签名账户的私钥; | |||
| * @return | |||
| */ | |||
| DigitalSignature sign(AsymmetricKeypair keyPair); | |||
| @@ -44,17 +41,22 @@ public interface PreparedTransaction extends HashObject { | |||
| /** | |||
| * 加入签名; | |||
| * | |||
| * @param address | |||
| * 签名账户的地址; | |||
| * @param digest | |||
| * Base64格式的签名摘要; | |||
| * @param address 签名账户的地址; | |||
| * @param digest Base64格式的签名摘要; | |||
| * @return | |||
| */ | |||
| void addSignature(DigitalSignature signature); | |||
| /** | |||
| * 生成交易请求; | |||
| * 提交交易请求到共识节点;<br> | |||
| * | |||
| * 这是同步方法,将阻塞当前线程,直到交易处理完成并返回结果之后,此方法才返回给调用者; | |||
| * | |||
| */ | |||
| TransactionResponse commit(); | |||
| /** | |||
| * 取消交易;<br> | |||
| */ | |||
| void cancel(); | |||
| } | |||
| @@ -4,8 +4,8 @@ import java.util.ArrayList; | |||
| import java.util.Collection; | |||
| import java.util.List; | |||
| import com.jd.blockchain.contract.EventResult; | |||
| import com.jd.blockchain.ledger.BlockchainIdentity; | |||
| import com.jd.blockchain.ledger.BytesValue; | |||
| import com.jd.blockchain.ledger.ContractCodeDeployOperation; | |||
| import com.jd.blockchain.ledger.ContractEventSendOperation; | |||
| import com.jd.blockchain.ledger.DataAccountKVSetOperation; | |||
| @@ -44,6 +44,7 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||
| private ContractInvocationProxyBuilder contractInvoProxyBuilder = new ContractInvocationProxyBuilder(); | |||
| // TODO: 暂时只支持单线程情形,未考虑多线程; | |||
| private List<Operation> operationList = new ArrayList<>(); | |||
| @Override | |||
| @@ -90,16 +91,43 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||
| return contractInvoProxyBuilder.create(address, contractIntf, contractEventSendOpBuilder); | |||
| } | |||
| @Override | |||
| public <T> EventResult<T> result(ContractEventExecutor execute) { | |||
| return contractInvoProxyBuilder.execute(execute); | |||
| } | |||
| /** | |||
| * 返回已经定义的操作列表; | |||
| * | |||
| * @return | |||
| */ | |||
| public Collection<Operation> getOperations() { | |||
| // TODO: 合并操作列表中可能的重复操作; | |||
| return operationList; | |||
| } | |||
| /** | |||
| * 返回与操作列表对应的返回值处理器; | |||
| * | |||
| * @return | |||
| */ | |||
| public Collection<OperationReturnValueHandler> getReturnValuetHandlers() { | |||
| List<OperationReturnValueHandler> resultHandlers = new ArrayList<OperationReturnValueHandler>(); | |||
| int index = 0; | |||
| for (Operation op : operationList) { | |||
| if (op instanceof ContractEventSendOperation) { | |||
| // 操作具有返回值,创建对应的结果处理器; | |||
| ContractEventSendOpTemplate opTemp = (ContractEventSendOpTemplate) op; | |||
| ContractInvocation invocation = opTemp.getInvocation(); | |||
| OperationReturnValueHandler retnHandler; | |||
| if (invocation == null) { | |||
| retnHandler = new NullOperationReturnValueHandler(index); | |||
| } else { | |||
| invocation.setOperationIndex(index); | |||
| retnHandler = invocation; | |||
| } | |||
| resultHandlers.add(retnHandler); | |||
| } | |||
| index++; | |||
| } | |||
| return resultHandlers; | |||
| } | |||
| public void clear() { | |||
| operationList.clear(); | |||
| } | |||
| @@ -160,13 +188,6 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||
| } | |||
| } | |||
| // @Override | |||
| // public DataAccountKVSetOperationBuilder set(String key, byte[] value, long expVersion) { | |||
| // innerBuilder.set(key, value, expVersion); | |||
| // addOperation(); | |||
| // return this; | |||
| // } | |||
| @Override | |||
| public DataAccountKVSetOperationBuilder setText(String key, String value, long expVersion) { | |||
| innerBuilder.setText(key, value, expVersion); | |||
| @@ -202,13 +223,6 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||
| return this; | |||
| } | |||
| // @Override | |||
| // public DataAccountKVSetOperationBuilder set(String key, String value, long expVersion) { | |||
| // innerBuilder.setText(key, value, expVersion); | |||
| // addOperation(); | |||
| // return this; | |||
| // } | |||
| @Override | |||
| public DataAccountKVSetOperationBuilder setJSON(String key, String value, long expVersion) { | |||
| innerBuilder.setJSON(key, value, expVersion); | |||
| @@ -233,7 +247,6 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||
| } | |||
| private class ContractCodeDeployOperationBuilderFilter implements ContractCodeDeployOperationBuilder { | |||
| @Override | |||
| public ContractCodeDeployOperation deploy(BlockchainIdentity id, byte[] chainCode) { | |||
| ContractCodeDeployOperation op = CONTRACT_CODE_DEPLOY_OP_BUILDER.deploy(id, chainCode); | |||
| @@ -250,25 +263,38 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||
| } | |||
| @Override | |||
| public ContractEventSendOperation send(Bytes address, String event, byte[] args) { | |||
| int opIndex = operationList.size(); | |||
| ContractEventSendOpTemplate op = new ContractEventSendOpTemplate(address, event, args, opIndex); | |||
| public synchronized ContractEventSendOperation send(Bytes address, String event, byte[] args) { | |||
| ContractEventSendOpTemplate op = new ContractEventSendOpTemplate(address, event, args); | |||
| operationList.add(op); | |||
| return op; | |||
| } | |||
| } | |||
| /** | |||
| * 不做任何操作的返回值处理器; | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| private static class NullOperationReturnValueHandler implements OperationReturnValueHandler { | |||
| private int operationIndex; | |||
| public NullOperationReturnValueHandler(int operationIndex) { | |||
| this.operationIndex = operationIndex; | |||
| } | |||
| @Override | |||
| public ContractEventSendOperation send(String address) { | |||
| return send(Bytes.fromBase58(address)); | |||
| public int getOperationIndex() { | |||
| return operationIndex; | |||
| } | |||
| @Override | |||
| public ContractEventSendOperation send(Bytes address) { | |||
| int opIndex = operationList.size(); | |||
| ContractEventSendOpTemplate op = new ContractEventSendOpTemplate(address, opIndex); | |||
| operationList.add(op); | |||
| return op; | |||
| public Object setReturnValue(BytesValue bytesValue) { | |||
| return null; | |||
| } | |||
| } | |||
| } | |||
| @@ -13,47 +13,13 @@ public class ContractEventSendOpTemplate implements ContractEventSendOperation { | |||
| private Bytes contractAddress; | |||
| private byte[] args; | |||
| private String event; | |||
| //交易操作时间; | |||
| private long txOpTime; | |||
| // 所属操作Index | |||
| private int opIndex; | |||
| public ContractEventSendOpTemplate() { | |||
| } | |||
| public ContractEventSendOpTemplate(Bytes contractAddress) { | |||
| this(contractAddress, -1); | |||
| } | |||
| public ContractEventSendOpTemplate(Bytes contractAddress, int opIndex) { | |||
| this.contractAddress = contractAddress; | |||
| this.opIndex = opIndex; | |||
| this.txOpTime = System.currentTimeMillis(); | |||
| } | |||
| private ContractInvocation invocation; | |||
| public ContractEventSendOpTemplate(Bytes contractAddress, String event, byte[] args) { | |||
| this(contractAddress, event, args, -1); | |||
| } | |||
| public ContractEventSendOpTemplate(Bytes contractAddress, String event, byte[] args, int opIndex) { | |||
| this.contractAddress = contractAddress; | |||
| this.event = event; | |||
| this.args = args; | |||
| this.opIndex = opIndex; | |||
| this.txOpTime = System.currentTimeMillis(); | |||
| } | |||
| public void setArgs(byte[] args) { | |||
| this.args = args; | |||
| } | |||
| public void setEvent(String event) { | |||
| this.event = event; | |||
| } | |||
| public void setEventAndArgs(String event, byte[] args) { | |||
| this.event = event; | |||
| this.args = args; | |||
| } | |||
| @Override | |||
| @@ -71,17 +37,12 @@ public class ContractEventSendOpTemplate implements ContractEventSendOperation { | |||
| return args; | |||
| } | |||
| @Override | |||
| public long getTxOpTime() { | |||
| return txOpTime; | |||
| public ContractInvocation getInvocation() { | |||
| return invocation; | |||
| } | |||
| /** | |||
| * 获取所属交易中的序号,该值不需要序列化 | |||
| * | |||
| * @return | |||
| */ | |||
| public int getOpIndex() { | |||
| return opIndex; | |||
| public void setInvocation(ContractInvocation invocation) { | |||
| this.invocation = invocation; | |||
| } | |||
| } | |||
| @@ -7,33 +7,17 @@ public interface ContractEventSendOperationBuilder { | |||
| /** | |||
| * @param address 合约地址; | |||
| * @param event 事件名; | |||
| * @param args 事件参数; | |||
| * @param event 事件名; | |||
| * @param args 事件参数; | |||
| * @return | |||
| */ | |||
| @Deprecated | |||
| ContractEventSendOperation send(String address, String event, byte[] args); | |||
| /** | |||
| * @param address 合约地址; | |||
| * @param event 事件名; | |||
| * @param args 事件参数; | |||
| * @param event 事件名; | |||
| * @param args 事件参数; | |||
| * @return | |||
| */ | |||
| @Deprecated | |||
| ContractEventSendOperation send(Bytes address, String event, byte[] args); | |||
| /** | |||
| * | |||
| * @param address | |||
| * @return | |||
| */ | |||
| ContractEventSendOperation send(String address); | |||
| /** | |||
| * | |||
| * @param address | |||
| * @return | |||
| */ | |||
| ContractEventSendOperation send(Bytes address); | |||
| } | |||
| @@ -0,0 +1,66 @@ | |||
| package com.jd.blockchain.transaction; | |||
| import java.lang.reflect.Method; | |||
| import java.util.concurrent.CompletableFuture; | |||
| import java.util.concurrent.Future; | |||
| import com.jd.blockchain.contract.ContractType; | |||
| import com.jd.blockchain.ledger.BytesValue; | |||
| /** | |||
| * ContractInvocation 包装了客户端发起的一次合约方法调用的相关信息,用于在客户端交易处理上下文进行共享处理状态; | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| class ContractInvocation implements OperationReturnValueHandler { | |||
| private Method method; | |||
| private ContractType contractType; | |||
| private int operationIndex = -1; | |||
| private CompletableFuture<Object> returnValueFuture; | |||
| public ContractInvocation(ContractType contractType, Method method) { | |||
| this.contractType = contractType; | |||
| this.method = method; | |||
| this.returnValueFuture = new CompletableFuture<Object>(); | |||
| } | |||
| public ContractType getContractType() { | |||
| return contractType; | |||
| } | |||
| @Override | |||
| public int getOperationIndex() { | |||
| return operationIndex; | |||
| } | |||
| public void setOperationIndex(int operationIndex) { | |||
| this.operationIndex = operationIndex; | |||
| } | |||
| public Class<?> getReturnType() { | |||
| return method.getReturnType(); | |||
| } | |||
| public Future<Object> getReturnValue() { | |||
| return returnValueFuture; | |||
| } | |||
| @Override | |||
| public Object setReturnValue(BytesValue bytesValue) { | |||
| // Resolve BytesValue to an value object with the return type; | |||
| Object returnValue = resolveValue(bytesValue, method.getReturnType()); | |||
| returnValueFuture.complete(returnValue); | |||
| return returnValue; | |||
| } | |||
| private Object resolveValue(BytesValue bytesValue, Class<?> returnType) { | |||
| // TODO: Resolve BytesValue to an value object with the return type; | |||
| throw new IllegalStateException("Not implemented!"); | |||
| } | |||
| } | |||
| @@ -0,0 +1,109 @@ | |||
| package com.jd.blockchain.transaction; | |||
| import java.lang.reflect.InvocationHandler; | |||
| import java.lang.reflect.Method; | |||
| import java.util.Arrays; | |||
| import com.jd.blockchain.contract.ContractException; | |||
| import com.jd.blockchain.contract.ContractSerializeUtils; | |||
| import com.jd.blockchain.contract.ContractType; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import com.jd.blockchain.utils.IllegalDataException; | |||
| public class ContractInvocationHandler implements InvocationHandler { | |||
| private Bytes contractAddress; | |||
| private ContractType contractType; | |||
| private ContractEventSendOperationBuilder sendOpBuilder; | |||
| private int proxyHashCode; | |||
| public ContractInvocationHandler(Bytes contractAddress, ContractType contractType, | |||
| ContractEventSendOperationBuilder sendOpBuilder) { | |||
| this.contractAddress = contractAddress; | |||
| if (contractType == null) { | |||
| throw new IllegalDataException("contractType == null, no invoke really."); | |||
| } | |||
| this.contractType = contractType; | |||
| this.sendOpBuilder = sendOpBuilder; | |||
| this.proxyHashCode = Arrays.deepHashCode(new Object[] { this, contractAddress, contractType, sendOpBuilder }); | |||
| } | |||
| @Override | |||
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |||
| // 判断是否是常规方法调用 | |||
| if (method.getName().equals("hashCode")) { | |||
| // 该处需要使用当前代理类的HashCode | |||
| return proxyHashCode; | |||
| } | |||
| if (method.getName().equals("toString")) { | |||
| // 该处使用当前代理类的toString | |||
| return this.toString(); | |||
| } | |||
| String event = contractType.getEvent(method); | |||
| if (event == null) { | |||
| // 该方法不是合约可执行的方法 | |||
| throw new ContractException( | |||
| String.format("The invoking method [%s] is not annotated as event handle method by @ContractEvent!", | |||
| method.toString())); | |||
| } | |||
| // 序列化调用参数; | |||
| byte[] argBytes = serializeArgs(args); | |||
| // 定义合约调用操作; | |||
| @SuppressWarnings("deprecation") | |||
| ContractEventSendOpTemplate opTemplate = (ContractEventSendOpTemplate) sendOpBuilder.send(contractAddress, | |||
| event, argBytes); | |||
| // 加入合约调用的额外信息; | |||
| ContractInvocation invocation = new ContractInvocation(contractType, method); | |||
| // 传递给定义操作的上下文,以便在生成交易时,同步操作在交易中的索引位置; | |||
| opTemplate.setInvocation(invocation); | |||
| // 传递给通过代理对象调用合约方法的调用者,以便可以同步操作在交易中的索引位置以及操作的返回值; | |||
| ContractInvocationStub.set(invocation); | |||
| // 返回类型的默认值 | |||
| return getDefaultValue(method.getReturnType()); | |||
| } | |||
| private byte[] serializeArgs(Object[] args) { | |||
| return ContractSerializeUtils.serializeArray(args); | |||
| } | |||
| private Object getDefaultValue(Class<?> returnType) { | |||
| if (returnType == void.class || returnType == Void.class) { | |||
| return null; | |||
| } | |||
| if (!returnType.isPrimitive()) { | |||
| // 非基本类型 | |||
| return null; | |||
| } else { | |||
| // 基本类型需要处理返回值,目前采用枚举遍历方式 | |||
| // 八种基本类型:int, double, float, long, short, boolean, byte, char, void | |||
| if (returnType.equals(int.class)) { | |||
| return 0; | |||
| } else if (returnType.equals(double.class)) { | |||
| return 0.0D; | |||
| } else if (returnType.equals(float.class)) { | |||
| return 0F; | |||
| } else if (returnType.equals(long.class)) { | |||
| return 0L; | |||
| } else if (returnType.equals(short.class)) { | |||
| return (short) 0; | |||
| } else if (returnType.equals(boolean.class)) { | |||
| return Boolean.FALSE; | |||
| } else if (returnType.equals(byte.class)) { | |||
| return (byte) 0; | |||
| } else if (returnType.equals(char.class)) { | |||
| return (char) 0; | |||
| } | |||
| return null; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,110 +0,0 @@ | |||
| package com.jd.blockchain.transaction; | |||
| import java.lang.reflect.InvocationHandler; | |||
| import java.lang.reflect.Method; | |||
| import com.jd.blockchain.contract.ContractSerializeUtils; | |||
| import com.jd.blockchain.contract.ContractType; | |||
| import com.jd.blockchain.ledger.ContractEventSendOperation; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import com.jd.blockchain.utils.IllegalDataException; | |||
| public class ContractInvocationProxy implements InvocationHandler { | |||
| // private String contractMessage; | |||
| // private Bytes contractAddress; | |||
| private ContractType contractType; | |||
| // private ContractEventSendOperationBuilder sendOpBuilder; | |||
| private ContractEventSendOperation sendOperation; | |||
| public ContractInvocationProxy(Bytes contractAddress, ContractType contractType, | |||
| ContractEventSendOperationBuilder sendOpBuilder) { | |||
| // this.contractAddress = contractAddress; | |||
| if(contractType == null){ | |||
| throw new IllegalDataException("contractType == null, no invoke really."); | |||
| } | |||
| this.contractType = contractType; | |||
| // this.sendOpBuilder = sendOpBuilder; | |||
| // Send一个地址,但不涉及Event | |||
| this.sendOperation = sendOpBuilder.send(contractAddress); | |||
| } | |||
| @Override | |||
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |||
| // 判断是否是常规方法调用 | |||
| if (method.getName().equals("hashCode")) { | |||
| // 该处需要使用当前代理类的HashCode | |||
| return this.hashCode(); | |||
| } | |||
| if (method.getName().equals("toString")) { | |||
| // 该处使用当前代理类的toString | |||
| return this.toString(); | |||
| } | |||
| if (sendOperation.getEvent() != null) { | |||
| throw new IllegalStateException("Contract Object can only execute method one time !!!"); | |||
| } | |||
| String event = contractType.getEvent(method); | |||
| if (event == null) { | |||
| // 该方法不是合约可执行的方法 | |||
| throw new IllegalAccessException(String.format("This Method [%s] is not Contract Event Method !!!", | |||
| method.getName())); | |||
| } | |||
| // 合约方法; | |||
| byte[] argBytes = serializeArgs(args); | |||
| if (sendOperation instanceof ContractEventSendOpTemplate) { | |||
| ((ContractEventSendOpTemplate) sendOperation).setEventAndArgs(event, argBytes); | |||
| } | |||
| // 代理操作,返回值类型无法创建 | |||
| return returnResult(method.getReturnType()); | |||
| } | |||
| private byte[] serializeArgs(Object[] args) { | |||
| return ContractSerializeUtils.serializeArray(args); | |||
| } | |||
| public int opIndex() { | |||
| if (sendOperation instanceof ContractEventSendOpTemplate) { | |||
| return ((ContractEventSendOpTemplate) sendOperation).getOpIndex(); | |||
| } | |||
| return -1; | |||
| } | |||
| private Object returnResult(Class<?> clazz) { | |||
| if (clazz.equals(Void.TYPE)) { | |||
| return null; | |||
| } | |||
| if (!clazz.isPrimitive()) { | |||
| // 非基本类型 | |||
| return null; | |||
| } else { | |||
| // 基本类型需要处理返回值,目前采用枚举遍历方式 | |||
| // 八种基本类型:int, double, float, long, short, boolean, byte, char, void | |||
| if (clazz.equals(int.class)) { | |||
| return 0; | |||
| } else if (clazz.equals(double.class)) { | |||
| return 0.0D; | |||
| } else if (clazz.equals(float.class)) { | |||
| return 0F; | |||
| } else if (clazz.equals(long.class)) { | |||
| return 0L; | |||
| } else if (clazz.equals(short.class)) { | |||
| return (short)0; | |||
| } else if (clazz.equals(boolean.class)) { | |||
| return Boolean.FALSE; | |||
| } else if (clazz.equals(byte.class)) { | |||
| return (byte)0; | |||
| } else if (clazz.equals(char.class)) { | |||
| return (char)0; | |||
| } | |||
| return null; | |||
| } | |||
| } | |||
| } | |||
| @@ -9,11 +9,17 @@ import com.jd.blockchain.contract.ContractType; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import com.jd.blockchain.utils.IllegalDataException; | |||
| /** | |||
| * 合约调用代理的构建器; | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| public class ContractInvocationProxyBuilder { | |||
| private Map<Class<?>, ContractType> contractTypes = new ConcurrentHashMap<>(); | |||
| private Map<Object, Integer> contractOperations = new ConcurrentHashMap<>(); | |||
| // private Map<Object, Integer> contractOperations = new ConcurrentHashMap<>(); | |||
| public <T> T create(String address, Class<T> contractIntf, ContractEventSendOperationBuilder contractEventBuilder) { | |||
| return create(Bytes.fromBase58(address), contractIntf, contractEventBuilder); | |||
| @@ -23,34 +29,34 @@ public class ContractInvocationProxyBuilder { | |||
| public <T> T create(Bytes address, Class<T> contractIntf, ContractEventSendOperationBuilder contractEventBuilder) { | |||
| ContractType contractType = resolveContractType(contractIntf); | |||
| ContractInvocationProxy proxyHandler = new ContractInvocationProxy(address, contractType, | |||
| contractEventBuilder); | |||
| ContractInvocationHandler proxyHandler = new ContractInvocationHandler(address, contractType, contractEventBuilder); | |||
| T proxy = (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), | |||
| new Class<?>[] { contractIntf }, proxyHandler); | |||
| // 创建关联关系 | |||
| contractOperations.put(proxy, proxyHandler.opIndex()); | |||
| // // 创建关联关系 | |||
| // contractOperations.put(proxy, proxyHandler.opIndex()); | |||
| return proxy; | |||
| } | |||
| public <T> EventResult<T> execute(ContractEventExecutor execute) { | |||
| Object contractProxy = execute.execute(); | |||
| if (contractProxy == null) { | |||
| // 该方法执行必须要有返回值 | |||
| throw new IllegalStateException( | |||
| String.format("ContractEventExecutor [%s] 's return must be not empty !!!", execute.toString())); | |||
| } | |||
| if (!(contractProxy instanceof Proxy)) { | |||
| throw new IllegalDataException( | |||
| String.format("ContractEventExecutor [%s] 's return must from TxTemplate.contract()'s result !!!", execute.toString())); | |||
| } | |||
| Integer opIndex = contractOperations.get(contractProxy); | |||
| if (opIndex != null && opIndex > -1) { | |||
| return new EventResult<>(opIndex); | |||
| } | |||
| return null; | |||
| } | |||
| // public <T> EventResult<T> execute(ContractEventExecutor execute) { | |||
| // Object contractProxy = execute.execute(); | |||
| // if (contractProxy == null) { | |||
| // // 该方法执行必须要有返回值 | |||
| // throw new IllegalStateException( | |||
| // String.format("ContractEventExecutor [%s] 's return must be not empty !!!", execute.toString())); | |||
| // } | |||
| // if (!(contractProxy instanceof Proxy)) { | |||
| // throw new IllegalDataException( | |||
| // String.format("ContractEventExecutor [%s] 's return must from TxTemplate.contract()'s result !!!", | |||
| // execute.toString())); | |||
| // } | |||
| // | |||
| // Integer opIndex = contractOperations.get(contractProxy); | |||
| // if (opIndex != null && opIndex > -1) { | |||
| // return new EventResult<>(opIndex); | |||
| // } | |||
| // return null; | |||
| // } | |||
| private ContractType resolveContractType(Class<?> contractIntf) { | |||
| ContractType contractType = contractTypes.get(contractIntf); | |||
| @@ -0,0 +1,33 @@ | |||
| package com.jd.blockchain.transaction; | |||
| /** | |||
| * 用于在上下文中传递合约调用返回值的工具类; | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| class ContractInvocationStub { | |||
| private static ThreadLocal<ContractInvocation> stub = new ThreadLocal<ContractInvocation>(); | |||
| private ContractInvocationStub() { | |||
| } | |||
| public static void set(ContractInvocation invocation) { | |||
| if (invocation == null) { | |||
| throw new IllegalArgumentException("Null stub value!"); | |||
| } | |||
| stub.set(invocation); | |||
| } | |||
| public static ContractInvocation take() { | |||
| ContractInvocation subValue = stub.get(); | |||
| if (subValue == null) { | |||
| throw new IllegalStateException( | |||
| "The latest invocation of contract has not been stubbed! It may be caused by the wrong call sequence from the upper layer!"); | |||
| } | |||
| stub.remove(); | |||
| return subValue; | |||
| } | |||
| } | |||
| @@ -0,0 +1,418 @@ | |||
| package com.jd.blockchain.transaction; | |||
| import java.util.concurrent.ExecutionException; | |||
| import java.util.concurrent.TimeUnit; | |||
| import java.util.concurrent.TimeoutException; | |||
| public class ContractReturns { | |||
| /** | |||
| * 解析合约方法调用的返回值; | |||
| * <p> | |||
| * 用法示例:<br> | |||
| * <code> | |||
| * ReturnValue<String> retnHolder = decode(contract.issue(assetKey, amount)); | |||
| * | |||
| * PreparedTransaction prepTx = tx.prepare(); | |||
| * prepTx.sign(userKey); | |||
| * prepTx.commit() | |||
| * | |||
| * String retnValue = retnHolder.get(); //这是同步方法,会阻塞当前线程等待交易提交后返回结果; | |||
| * </code> | |||
| * | |||
| * @param <T> | |||
| * @param call | |||
| * @return | |||
| */ | |||
| public static <T> ReturnValue<T> decode(T call) { | |||
| ContractInvocation invocation = ContractInvocationStub.take(); | |||
| return new ReturnValue<T>(invocation); | |||
| } | |||
| /** | |||
| * 解析合约方法调用的返回值; | |||
| * <p> | |||
| * 用法示例:<br> | |||
| * <code> | |||
| * ReturnLongValue retnHolder = decode(contract.issue(assetKey, amount)); | |||
| * | |||
| * PreparedTransaction prepTx = tx.prepare(); | |||
| * prepTx.sign(userKey); | |||
| * prepTx.commit() | |||
| * | |||
| * long retnValue = retnHolder.get(); //这是同步方法,会阻塞当前线程等待交易提交后返回结果; | |||
| * </code> | |||
| * | |||
| * @param call | |||
| * @return | |||
| */ | |||
| public static ReturnLongValue decode(long call) { | |||
| ContractInvocation invocation = ContractInvocationStub.take(); | |||
| return new ReturnLongValue(invocation); | |||
| } | |||
| /** | |||
| * 解析合约方法调用的返回值; | |||
| * <p> | |||
| * 用法示例:<br> | |||
| * <code> | |||
| * ReturnLongValue retnHolder = decode(contract.issue(assetKey, amount)); | |||
| * | |||
| * PreparedTransaction prepTx = tx.prepare(); | |||
| * prepTx.sign(userKey); | |||
| * prepTx.commit() | |||
| * | |||
| * int retnValue = retnHolder.get(); //这是同步方法,会阻塞当前线程等待交易提交后返回结果; | |||
| * </code> | |||
| * | |||
| * @param call | |||
| * @return | |||
| */ | |||
| public static ReturnIntValue decode(int call) { | |||
| ContractInvocation invocation = ContractInvocationStub.take(); | |||
| return new ReturnIntValue(invocation); | |||
| } | |||
| /** | |||
| * 解析合约方法调用的返回值; | |||
| * <p> | |||
| * 用法示例:<br> | |||
| * <code> | |||
| * ReturnLongValue retnHolder = decode(contract.issue(assetKey, amount)); | |||
| * | |||
| * PreparedTransaction prepTx = tx.prepare(); | |||
| * prepTx.sign(userKey); | |||
| * prepTx.commit() | |||
| * | |||
| * short retnValue = retnHolder.get(); //这是同步方法,会阻塞当前线程等待交易提交后返回结果; | |||
| * </code> | |||
| * | |||
| * @param call | |||
| * @return | |||
| */ | |||
| public static ReturnShortValue decode(short call) { | |||
| ContractInvocation invocation = ContractInvocationStub.take(); | |||
| return new ReturnShortValue(invocation); | |||
| } | |||
| /** | |||
| * 解析合约方法调用的返回值; | |||
| * <p> | |||
| * 用法示例:<br> | |||
| * <code> | |||
| * ReturnLongValue retnHolder = decode(contract.issue(assetKey, amount)); | |||
| * | |||
| * PreparedTransaction prepTx = tx.prepare(); | |||
| * prepTx.sign(userKey); | |||
| * prepTx.commit() | |||
| * | |||
| * byte retnValue = retnHolder.get(); //这是同步方法,会阻塞当前线程等待交易提交后返回结果; | |||
| * </code> | |||
| * | |||
| * @param call | |||
| * @return | |||
| */ | |||
| public static ReturnByteValue decode(byte call) { | |||
| ContractInvocation invocation = ContractInvocationStub.take(); | |||
| return new ReturnByteValue(invocation); | |||
| } | |||
| /** | |||
| * 解析合约方法调用的返回值; | |||
| * <p> | |||
| * 用法示例:<br> | |||
| * <code> | |||
| * ReturnLongValue retnHolder = decode(contract.issue(assetKey, amount)); | |||
| * | |||
| * PreparedTransaction prepTx = tx.prepare(); | |||
| * prepTx.sign(userKey); | |||
| * prepTx.commit() | |||
| * | |||
| * boolean retnValue = retnHolder.get(); //这是同步方法,会阻塞当前线程等待交易提交后返回结果; | |||
| * </code> | |||
| * | |||
| * @param call | |||
| * @return | |||
| */ | |||
| public static ReturnBooleanValue decode(boolean call) { | |||
| ContractInvocation invocation = ContractInvocationStub.take(); | |||
| return new ReturnBooleanValue(invocation); | |||
| } | |||
| //----------------------- 内部类型 ----------------------- | |||
| private static class ReturnValueBase { | |||
| private ContractInvocation invocation; | |||
| private ReturnValueBase(ContractInvocation invocation) { | |||
| this.invocation = invocation; | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @return | |||
| */ | |||
| private Object get() { | |||
| try { | |||
| return invocation.getReturnValue().get(); | |||
| } catch (InterruptedException | ExecutionException e) { | |||
| throw new IllegalStateException(e.getMessage(), e); | |||
| } | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @param unit | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| private Object get(long timeout, TimeUnit unit) throws TimeoutException { | |||
| try { | |||
| return invocation.getReturnValue().get(timeout, unit); | |||
| } catch (InterruptedException | ExecutionException e) { | |||
| throw new IllegalStateException(e.getMessage(), e); | |||
| } | |||
| } | |||
| } | |||
| public static class ReturnValue<T> extends ReturnValueBase { | |||
| private ReturnValue(ContractInvocation invocation) { | |||
| super(invocation); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @return | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public T get() { | |||
| return (T) super.get(); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| public T get(long timeout) throws TimeoutException { | |||
| return get(timeout, TimeUnit.MILLISECONDS); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @param unit | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public T get(long timeout, TimeUnit unit) throws TimeoutException { | |||
| return (T) super.get(timeout, unit); | |||
| } | |||
| } | |||
| public static class ReturnLongValue extends ReturnValueBase { | |||
| private ReturnLongValue(ContractInvocation invocation) { | |||
| super(invocation); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @return | |||
| */ | |||
| public long get() { | |||
| return (long) super.get(); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| public long get(long timeout) throws TimeoutException { | |||
| return get(timeout, TimeUnit.MILLISECONDS); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @param unit | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| public long get(long timeout, TimeUnit unit) throws TimeoutException { | |||
| return (long) super.get(timeout, unit); | |||
| } | |||
| } | |||
| public static class ReturnIntValue extends ReturnValueBase { | |||
| private ReturnIntValue(ContractInvocation invocation) { | |||
| super(invocation); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @return | |||
| */ | |||
| public int get() { | |||
| return (int) super.get(); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| public int get(long timeout) throws TimeoutException { | |||
| return get(timeout, TimeUnit.MILLISECONDS); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @param unit | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| public int get(long timeout, TimeUnit unit) throws TimeoutException { | |||
| return (int) super.get(timeout, unit); | |||
| } | |||
| } | |||
| public static class ReturnShortValue extends ReturnValueBase { | |||
| private ReturnShortValue(ContractInvocation invocation) { | |||
| super(invocation); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @return | |||
| */ | |||
| public short get() { | |||
| return (short) super.get(); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| public short get(long timeout) throws TimeoutException { | |||
| return get(timeout, TimeUnit.MILLISECONDS); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @param unit | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| public short get(long timeout, TimeUnit unit) throws TimeoutException { | |||
| return (short) super.get(timeout, unit); | |||
| } | |||
| } | |||
| public static class ReturnByteValue extends ReturnValueBase { | |||
| private ReturnByteValue(ContractInvocation invocation) { | |||
| super(invocation); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @return | |||
| */ | |||
| public byte get() { | |||
| return (byte) super.get(); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| public byte get(long timeout) throws TimeoutException { | |||
| return get(timeout, TimeUnit.MILLISECONDS); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @param unit | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| public byte get(long timeout, TimeUnit unit) throws TimeoutException { | |||
| return (byte) super.get(timeout, unit); | |||
| } | |||
| } | |||
| public static class ReturnBooleanValue extends ReturnValueBase { | |||
| private ReturnBooleanValue(ContractInvocation invocation) { | |||
| super(invocation); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @return | |||
| */ | |||
| public boolean get() { | |||
| return (boolean) super.get(); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| public boolean get(long timeout) throws TimeoutException { | |||
| return get(timeout, TimeUnit.MILLISECONDS); | |||
| } | |||
| /** | |||
| * 等待结果合约调用的结果返回; | |||
| * | |||
| * @param timeout | |||
| * @param unit | |||
| * @return | |||
| * @throws TimeoutException | |||
| */ | |||
| public boolean get(long timeout, TimeUnit unit) throws TimeoutException { | |||
| return (boolean) super.get(timeout, unit); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,18 +1,9 @@ | |||
| package com.jd.blockchain.transaction; | |||
| import com.jd.blockchain.contract.EventResult; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| public interface EventOperator { | |||
| // /** | |||
| // * 合约事件; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // @Deprecated | |||
| // ContractEventSendOperationBuilder contractEvents(); | |||
| /** | |||
| * 创建调用合约的代理实例; | |||
| * | |||
| @@ -31,11 +22,11 @@ public interface EventOperator { | |||
| */ | |||
| <T> T contract(Bytes address, Class<T> contractIntf); | |||
| /** | |||
| * 执行合约异步等待应答结果 | |||
| * | |||
| * @param execute | |||
| * @return | |||
| */ | |||
| <T> EventResult<T> result(ContractEventExecutor execute); | |||
| // /** | |||
| // * 执行合约异步等待应答结果 | |||
| // * | |||
| // * @param execute | |||
| // * @return | |||
| // */ | |||
| // <T> EventResult<T> result(ContractEventExecutor execute); | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| package com.jd.blockchain.transaction; | |||
| import com.jd.blockchain.ledger.BytesValue; | |||
| interface OperationReturnValueHandler { | |||
| int getOperationIndex(); | |||
| Object setReturnValue(BytesValue bytesValue); | |||
| } | |||
| @@ -1,17 +1,23 @@ | |||
| package com.jd.blockchain.transaction; | |||
| import java.util.Arrays; | |||
| import java.util.Collection; | |||
| import java.util.Comparator; | |||
| import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
| import com.jd.blockchain.contract.ContractSerializeUtils; | |||
| import com.jd.blockchain.contract.EventResult; | |||
| import com.jd.blockchain.crypto.AsymmetricKeypair; | |||
| import com.jd.blockchain.crypto.Crypto; | |||
| import com.jd.blockchain.crypto.HashDigest; | |||
| import com.jd.blockchain.crypto.PrivKey; | |||
| import com.jd.blockchain.crypto.SignatureDigest; | |||
| import com.jd.blockchain.crypto.SignatureFunction; | |||
| import com.jd.blockchain.ledger.*; | |||
| import java.util.Map; | |||
| import com.jd.blockchain.ledger.DigitalSignature; | |||
| import com.jd.blockchain.ledger.OperationResult; | |||
| import com.jd.blockchain.ledger.PreparedTransaction; | |||
| import com.jd.blockchain.ledger.TransactionContent; | |||
| import com.jd.blockchain.ledger.TransactionRequest; | |||
| import com.jd.blockchain.ledger.TransactionRequestBuilder; | |||
| import com.jd.blockchain.ledger.TransactionResponse; | |||
| public class PreparedTx implements PreparedTransaction { | |||
| @@ -19,16 +25,29 @@ public class PreparedTx implements PreparedTransaction { | |||
| private TransactionService txProcessor; | |||
| private Map<Integer, EventResult> eventResults; | |||
| private OperationReturnValueHandler[] opReturnValueHandlers; | |||
| public PreparedTx(TransactionRequestBuilder txReqBuilder, TransactionService txProcessor) { | |||
| this(txReqBuilder, txProcessor, null); | |||
| } | |||
| public PreparedTx(TransactionRequestBuilder txReqBuilder, TransactionService txProcessor, Map<Integer, EventResult> eventResults) { | |||
| /** | |||
| * 创建一个“就绪交易”对象; | |||
| * | |||
| * @param txReqBuilder 交易请求构建器; | |||
| * @param txProcessor 交易处理服务; | |||
| * @param opReturnValueHandlerList 操作返回值处理器列表; | |||
| */ | |||
| public PreparedTx(TransactionRequestBuilder txReqBuilder, TransactionService txProcessor, | |||
| Collection<OperationReturnValueHandler> opReturnValueHandlerList) { | |||
| this.txReqBuilder = txReqBuilder; | |||
| this.txProcessor = txProcessor; | |||
| this.eventResults = eventResults; | |||
| this.opReturnValueHandlers = opReturnValueHandlerList | |||
| .toArray(new OperationReturnValueHandler[opReturnValueHandlerList.size()]); | |||
| // 按照操作索引升序排列; | |||
| Arrays.sort(opReturnValueHandlers, new Comparator<OperationReturnValueHandler>() { | |||
| @Override | |||
| public int compare(OperationReturnValueHandler o1, OperationReturnValueHandler o2) { | |||
| return o1.getOperationIndex() - o2.getOperationIndex(); | |||
| } | |||
| }); | |||
| } | |||
| @Override | |||
| @@ -59,23 +78,32 @@ public class PreparedTx implements PreparedTransaction { | |||
| @Override | |||
| public TransactionResponse commit() { | |||
| TransactionRequest txReq = txReqBuilder.buildRequest(); | |||
| // 发起交易请求; | |||
| TransactionResponse txResponse = txProcessor.process(txReq); | |||
| // 重新包装操作集合 | |||
| OperationResult[] operationResults = txResponse.getOperationResults(); | |||
| if (operationResults != null && operationResults.length > 0 && | |||
| eventResults != null && !eventResults.isEmpty()) { | |||
| for (OperationResult operationResult : operationResults) { | |||
| int opIndex = operationResult.getIndex(); | |||
| EventResult eventResult = eventResults.get(opIndex); | |||
| if (eventResult != null) { | |||
| eventResult.done(ContractSerializeUtils.resolve(operationResult.getResult())); | |||
| try { | |||
| TransactionRequest txReq = txReqBuilder.buildRequest(); | |||
| // 发起交易请求; | |||
| TransactionResponse txResponse = txProcessor.process(txReq); | |||
| // 解析返回值;正常的情况下,返回结果列表与结果处理器列表中元素对应的操作索引是一致的; | |||
| OperationResult[] opResults = txResponse.getOperationResults(); | |||
| if (opResults != null && opResults.length > 0) { | |||
| if (opResults.length != opReturnValueHandlers.length) { | |||
| throw new IllegalStateException(String.format( | |||
| "The operation result list of tx doesn't match it's return value handler list! --[TX.Content.Hash=%s][NumOfResults=%s][NumOfHandlers=%s]", | |||
| txReq.getTransactionContent().getHash(), opResults.length, opReturnValueHandlers.length)); | |||
| } | |||
| for (int i = 0; i < opResults.length; i++) { | |||
| if (opResults[i].getIndex() != opReturnValueHandlers[i].getOperationIndex()) { | |||
| throw new IllegalStateException( | |||
| "The operation indexes of the items in the result list and in the handler list don't match!"); | |||
| } | |||
| opReturnValueHandlers[i].setReturnValue(opResults[i].getResult()); | |||
| } | |||
| } | |||
| return txResponse; | |||
| } catch (Exception e) { | |||
| //TODO: 出错时清理交易上下文,释放与交易关联对异步等待资源,避免当前线程死锁; | |||
| } | |||
| return txResponse; | |||
| } | |||
| } | |||
| @@ -1,8 +1,9 @@ | |||
| package com.jd.blockchain.transaction; | |||
| import java.util.Collection; | |||
| import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
| import com.jd.blockchain.contract.EventResult; | |||
| import com.jd.blockchain.crypto.Crypto; | |||
| import com.jd.blockchain.crypto.HashDigest; | |||
| import com.jd.blockchain.ledger.TransactionBuilder; | |||
| @@ -11,9 +12,6 @@ import com.jd.blockchain.ledger.TransactionContentBody; | |||
| import com.jd.blockchain.ledger.TransactionRequestBuilder; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| public class TxBuilder implements TransactionBuilder { | |||
| static { | |||
| @@ -22,8 +20,6 @@ public class TxBuilder implements TransactionBuilder { | |||
| private BlockchainOperationFactory opFactory = new BlockchainOperationFactory(); | |||
| private Map<Object, Integer> contractProxyMap = new HashMap<>(); | |||
| private static final String DEFAULT_HASH_ALGORITHM = "SHA256"; | |||
| private HashDigest ledgerHash; | |||
| @@ -55,6 +51,10 @@ public class TxBuilder implements TransactionBuilder { | |||
| return txContent; | |||
| } | |||
| public Collection<OperationReturnValueHandler> getReturnValuehandlers() { | |||
| return opFactory.getReturnValuetHandlers(); | |||
| } | |||
| @Override | |||
| public LedgerInitOperationBuilder ledgers() { | |||
| return opFactory.ledgers(); | |||
| @@ -94,11 +94,6 @@ public class TxBuilder implements TransactionBuilder { | |||
| return opFactory.contract(address, contractIntf); | |||
| } | |||
| @Override | |||
| public <T> EventResult<T> result(ContractEventExecutor execute) { | |||
| return opFactory.result(execute); | |||
| } | |||
| @Override | |||
| public <T> T contract(String address, Class<T> contractIntf) { | |||
| return opFactory.contract(address, contractIntf); | |||
| @@ -71,8 +71,7 @@ public class TxRequestBuilder implements TransactionRequestBuilder { | |||
| } | |||
| public static SignatureDigest sign(TransactionContent txContent, PrivKey privKey) { | |||
| return Crypto.getSignatureFunction(privKey.getAlgorithm()).sign(privKey, | |||
| txContent.getHash().toBytes()); | |||
| return Crypto.getSignatureFunction(privKey.getAlgorithm()).sign(privKey, txContent.getHash().toBytes()); | |||
| } | |||
| public static boolean verifySignature(TransactionContent txContent, SignatureDigest signDigest, PubKey pubKey) { | |||
| @@ -1,27 +1,21 @@ | |||
| package com.jd.blockchain.transaction; | |||
| import com.jd.blockchain.contract.EventResult; | |||
| import com.jd.blockchain.crypto.HashDigest; | |||
| import com.jd.blockchain.ledger.PreparedTransaction; | |||
| import com.jd.blockchain.ledger.TransactionRequestBuilder; | |||
| import com.jd.blockchain.ledger.TransactionTemplate; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| public class TxTemplate implements TransactionTemplate { | |||
| private TxBuilder txBuilder; | |||
| private TransactionService txService; | |||
| private Map<Integer, EventResult> eventResults; | |||
| public TxTemplate(HashDigest ledgerHash, TransactionService txService) { | |||
| this.txBuilder = new TxBuilder(ledgerHash); | |||
| this.txService = txService; | |||
| this.eventResults = new HashMap<>(); | |||
| } | |||
| @Override | |||
| @@ -32,7 +26,7 @@ public class TxTemplate implements TransactionTemplate { | |||
| @Override | |||
| public PreparedTransaction prepare() { | |||
| TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||
| return new PreparedTx(txReqBuilder, txService, eventResults); | |||
| return new PreparedTx(txReqBuilder, txService, txBuilder.getReturnValuehandlers()); | |||
| } | |||
| @Override | |||
| @@ -60,25 +54,11 @@ public class TxTemplate implements TransactionTemplate { | |||
| return txBuilder.contracts(); | |||
| } | |||
| // @Override | |||
| // public ContractEventSendOperationBuilder contractEvents() { | |||
| // return txBuilder.contractEvents(); | |||
| // } | |||
| @Override | |||
| public <T> T contract(Bytes address, Class<T> contractIntf) { | |||
| return txBuilder.contract(address, contractIntf); | |||
| } | |||
| @Override | |||
| public <T> EventResult<T> result(ContractEventExecutor execute) { | |||
| EventResult<T> eventResult = txBuilder.result(execute); | |||
| if (eventResult != null) { | |||
| eventResults.put(eventResult.opIndex(), eventResult); | |||
| } | |||
| return eventResult; | |||
| } | |||
| @Override | |||
| public <T> T contract(String address, Class<T> contractIntf) { | |||
| return txBuilder.contract(address, contractIntf); | |||
| @@ -1,164 +1,159 @@ | |||
| package com.jd.blockchain.sdk.samples; | |||
| import static com.jd.blockchain.sdk.samples.SDKDemo_Constant.readChainCodes; | |||
| import static com.jd.blockchain.transaction.ContractReturns.decode; | |||
| import com.jd.blockchain.contract.EventResult; | |||
| import com.jd.blockchain.contract.TransferContract; | |||
| import com.jd.blockchain.ledger.*; | |||
| import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||
| import com.jd.blockchain.ledger.BlockchainKeypair; | |||
| import com.jd.blockchain.ledger.KVDataEntry; | |||
| import com.jd.blockchain.ledger.PreparedTransaction; | |||
| import com.jd.blockchain.ledger.TransactionResponse; | |||
| import com.jd.blockchain.ledger.TransactionTemplate; | |||
| import com.jd.blockchain.transaction.ContractEventExecutor; | |||
| import com.jd.blockchain.transaction.ContractReturns.ReturnLongValue; | |||
| import com.jd.blockchain.transaction.ContractReturns.ReturnValue; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import static com.jd.blockchain.sdk.samples.SDKDemo_Constant.readChainCodes; | |||
| public class SDK_Contract_Demo extends SDK_Base_Demo { | |||
| public static void main(String[] args) { | |||
| SDK_Contract_Demo demo = new SDK_Contract_Demo(); | |||
| demo.executeContract(); | |||
| } | |||
| public void executeContract() { | |||
| // 发布jar包 | |||
| // 定义交易模板 | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 将jar包转换为二进制数据 | |||
| byte[] contractCode = readChainCodes("transfer.jar"); | |||
| // 生成一个合约账号 | |||
| BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | |||
| // 生成发布合约操作 | |||
| txTpl.contracts().deploy(contractDeployKey.getIdentity(), contractCode); | |||
| // 生成预发布交易; | |||
| PreparedTransaction ptx = txTpl.prepare(); | |||
| // 对交易进行签名 | |||
| ptx.sign(adminKey); | |||
| // 提交并等待共识返回; | |||
| TransactionResponse txResp = ptx.commit(); | |||
| // 获取合约地址 | |||
| Bytes contractAddress = contractDeployKey.getAddress(); | |||
| // 打印交易返回信息 | |||
| System.out.printf("Tx[%s] -> BlockHeight = %s, BlockHash = %s, isSuccess = %s, ExecutionState = %s \r\n", | |||
| txResp.getContentHash().toBase58(), txResp.getBlockHeight(), | |||
| txResp.getBlockHash().toBase58(), txResp.isSuccess(), | |||
| txResp.getExecutionState()); | |||
| // 打印合约地址 | |||
| System.out.printf("ContractAddress = %s \r\n", contractAddress.toBase58()); | |||
| // 注册一个数据账户 | |||
| BlockchainKeypair dataAccount = createDataAccount(); | |||
| // 获取数据账户地址 | |||
| String dataAddress = dataAccount.getAddress().toBase58(); | |||
| // 打印数据账户地址 | |||
| System.out.printf("DataAccountAddress = %s \r\n", dataAddress); | |||
| // 创建两个账号: | |||
| String account0 = "jd_zhangsan", account1 = "jd_lisi"; | |||
| long account0Money = 3000L, account1Money = 2000L; | |||
| // 创建两个账户 | |||
| // 使用KV操作创建一个账户 | |||
| System.out.println(create(dataAddress, account0, account0Money, false, null)); | |||
| // 使用合约创建一个账户 | |||
| System.out.println(create(dataAddress, account1, account1Money, true, contractAddress)); | |||
| // 转账,使得双方钱达到一致 | |||
| System.out.println(transfer(dataAddress, account0, account1, 500L, contractAddress)); | |||
| // 通过合约读取account0的当前信息 | |||
| System.out.printf("Read DataAccountAddress[%s] Account = %s 's money = %s (By Contract)\r\n", | |||
| dataAddress, account0, readByContract(dataAddress, account0, contractAddress)); | |||
| // 通过KV读取account1的当前信息 | |||
| System.out.printf("Read DataAccountAddress[%s] Account = %s 's money = %s (By KV Operation)\r\n", | |||
| dataAddress, account1, readByKvOperation(dataAddress, account1)); | |||
| // 通过合约读取account0的历史信息 | |||
| System.out.println(readAll(dataAddress, account0, contractAddress)); | |||
| // 通过合约读取account1的历史信息 | |||
| System.out.println(readAll(dataAddress, account1, contractAddress)); | |||
| } | |||
| private String readAll(String address, String account, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| EventResult<String> eventResult = txTpl.result((ContractEventExecutor<TransferContract>) () -> { | |||
| transferContract.readAll(address, account); | |||
| return transferContract; | |||
| }); | |||
| commit(txTpl); | |||
| return eventResult.get(); | |||
| } | |||
| private long readByContract(String address, String account, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| EventResult<Long> eventResult = txTpl.result((ContractEventExecutor<TransferContract>) () -> { | |||
| transferContract.read(address, account); | |||
| return transferContract; | |||
| }); | |||
| commit(txTpl); | |||
| return eventResult.get(); | |||
| } | |||
| private long readByKvOperation(String address, String account) { | |||
| KVDataEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, address, account); | |||
| if (kvDataEntries == null || kvDataEntries.length == 0) { | |||
| throw new IllegalStateException(String.format("Ledger %s Service inner Error !!!", ledgerHash.toBase58())); | |||
| } | |||
| KVDataEntry kvDataEntry = kvDataEntries[0]; | |||
| if (kvDataEntry.getVersion() == -1) { | |||
| return 0L; | |||
| } | |||
| return (long) (kvDataEntry.getValue()); | |||
| } | |||
| private String transfer(String address, String from, String to, long money, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| EventResult<String> eventResult = txTpl.result((ContractEventExecutor<TransferContract>) () -> { | |||
| transferContract.transfer(address, from, to, money); | |||
| return transferContract; | |||
| }); | |||
| commit(txTpl); | |||
| return eventResult.get(); | |||
| } | |||
| private BlockchainKeypair createDataAccount() { | |||
| // 首先注册一个数据账户 | |||
| BlockchainKeypair newDataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| txTpl.dataAccounts().register(newDataAccount.getIdentity()); | |||
| commit(txTpl); | |||
| return newDataAccount; | |||
| } | |||
| private String create(String address, String account, long money, boolean useContract, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| if (useContract) { | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| EventResult<String> eventResult = txTpl.result((ContractEventExecutor<TransferContract>) () -> { | |||
| transferContract.create(address, account, money); | |||
| return transferContract; | |||
| }); | |||
| commit(txTpl); | |||
| return eventResult.get(); | |||
| } else { | |||
| // 通过KV创建 | |||
| txTpl.dataAccount(address).setInt64(account, money, -1); | |||
| TransactionResponse txResp = commit(txTpl); | |||
| return String.format("DataAccountAddress[%s] -> Create(By KV Operation) Account = %s and Money = %s Success!!! \r\n", | |||
| address, account, money); | |||
| } | |||
| } | |||
| public static void main(String[] args) { | |||
| SDK_Contract_Demo demo = new SDK_Contract_Demo(); | |||
| demo.executeContract(); | |||
| } | |||
| public void executeContract() { | |||
| // 发布jar包 | |||
| // 定义交易模板 | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 将jar包转换为二进制数据 | |||
| byte[] contractCode = readChainCodes("transfer.jar"); | |||
| // 生成一个合约账号 | |||
| BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | |||
| // 生成发布合约操作 | |||
| txTpl.contracts().deploy(contractDeployKey.getIdentity(), contractCode); | |||
| // 生成预发布交易; | |||
| PreparedTransaction ptx = txTpl.prepare(); | |||
| // 对交易进行签名 | |||
| ptx.sign(adminKey); | |||
| // 提交并等待共识返回; | |||
| TransactionResponse txResp = ptx.commit(); | |||
| // 获取合约地址 | |||
| Bytes contractAddress = contractDeployKey.getAddress(); | |||
| // 打印交易返回信息 | |||
| System.out.printf("Tx[%s] -> BlockHeight = %s, BlockHash = %s, isSuccess = %s, ExecutionState = %s \r\n", | |||
| txResp.getContentHash().toBase58(), txResp.getBlockHeight(), txResp.getBlockHash().toBase58(), | |||
| txResp.isSuccess(), txResp.getExecutionState()); | |||
| // 打印合约地址 | |||
| System.out.printf("ContractAddress = %s \r\n", contractAddress.toBase58()); | |||
| // 注册一个数据账户 | |||
| BlockchainKeypair dataAccount = createDataAccount(); | |||
| // 获取数据账户地址 | |||
| String dataAddress = dataAccount.getAddress().toBase58(); | |||
| // 打印数据账户地址 | |||
| System.out.printf("DataAccountAddress = %s \r\n", dataAddress); | |||
| // 创建两个账号: | |||
| String account0 = "jd_zhangsan", account1 = "jd_lisi"; | |||
| long account0Money = 3000L, account1Money = 2000L; | |||
| // 创建两个账户 | |||
| // 使用KV操作创建一个账户 | |||
| System.out.println(create(dataAddress, account0, account0Money, false, null)); | |||
| // 使用合约创建一个账户 | |||
| System.out.println(create(dataAddress, account1, account1Money, true, contractAddress)); | |||
| // 转账,使得双方钱达到一致 | |||
| System.out.println(transfer(dataAddress, account0, account1, 500L, contractAddress)); | |||
| // 通过合约读取account0的当前信息 | |||
| System.out.printf("Read DataAccountAddress[%s] Account = %s 's money = %s (By Contract)\r\n", dataAddress, | |||
| account0, readByContract(dataAddress, account0, contractAddress)); | |||
| // 通过KV读取account1的当前信息 | |||
| System.out.printf("Read DataAccountAddress[%s] Account = %s 's money = %s (By KV Operation)\r\n", dataAddress, | |||
| account1, readByKvOperation(dataAddress, account1)); | |||
| // 通过合约读取account0的历史信息 | |||
| System.out.println(readAll(dataAddress, account0, contractAddress)); | |||
| // 通过合约读取account1的历史信息 | |||
| System.out.println(readAll(dataAddress, account1, contractAddress)); | |||
| } | |||
| private String readAll(String address, String account, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| ReturnValue<String> result = decode(transferContract.readAll(address, account)); | |||
| commit(txTpl); | |||
| return result.get(); | |||
| } | |||
| private long readByContract(String address, String account, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| ReturnLongValue result = decode(transferContract.read(address, account)); | |||
| commit(txTpl); | |||
| return result.get(); | |||
| } | |||
| private long readByKvOperation(String address, String account) { | |||
| KVDataEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, address, account); | |||
| if (kvDataEntries == null || kvDataEntries.length == 0) { | |||
| throw new IllegalStateException(String.format("Ledger %s Service inner Error !!!", ledgerHash.toBase58())); | |||
| } | |||
| KVDataEntry kvDataEntry = kvDataEntries[0]; | |||
| if (kvDataEntry.getVersion() == -1) { | |||
| return 0L; | |||
| } | |||
| return (long) (kvDataEntry.getValue()); | |||
| } | |||
| private String transfer(String address, String from, String to, long money, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| ReturnValue<String> result = decode(transferContract.transfer(address, from, to, money)); | |||
| commit(txTpl); | |||
| return result.get(); | |||
| } | |||
| private BlockchainKeypair createDataAccount() { | |||
| // 首先注册一个数据账户 | |||
| BlockchainKeypair newDataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| txTpl.dataAccounts().register(newDataAccount.getIdentity()); | |||
| commit(txTpl); | |||
| return newDataAccount; | |||
| } | |||
| private String create(String address, String account, long money, boolean useContract, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| if (useContract) { | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| ReturnValue<String> result = decode(transferContract.create(address, account, money)); | |||
| commit(txTpl); | |||
| return result.get(); | |||
| } else { | |||
| // 通过KV创建 | |||
| txTpl.dataAccount(address).setInt64(account, money, -1); | |||
| TransactionResponse txResp = commit(txTpl); | |||
| return String.format( | |||
| "DataAccountAddress[%s] -> Create(By KV Operation) Account = %s and Money = %s Success!!! \r\n", | |||
| address, account, money); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,171 +1,165 @@ | |||
| package test.com.jd.blockchain.sdk.test; | |||
| import com.jd.blockchain.contract.EventResult; | |||
| import static com.jd.blockchain.sdk.samples.SDKDemo_Constant.readChainCodes; | |||
| import static com.jd.blockchain.transaction.ContractReturns.decode; | |||
| import org.junit.Before; | |||
| import org.junit.Test; | |||
| import com.jd.blockchain.contract.TransferContract; | |||
| import com.jd.blockchain.crypto.HashDigest; | |||
| import com.jd.blockchain.crypto.PrivKey; | |||
| import com.jd.blockchain.crypto.PubKey; | |||
| import com.jd.blockchain.ledger.*; | |||
| import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||
| import com.jd.blockchain.ledger.BlockchainKeypair; | |||
| import com.jd.blockchain.ledger.PreparedTransaction; | |||
| import com.jd.blockchain.ledger.TransactionResponse; | |||
| import com.jd.blockchain.ledger.TransactionTemplate; | |||
| import com.jd.blockchain.sdk.BlockchainService; | |||
| import com.jd.blockchain.sdk.client.GatewayServiceFactory; | |||
| import com.jd.blockchain.sdk.samples.SDKDemo_Constant; | |||
| import com.jd.blockchain.tools.keygen.KeyGenCommand; | |||
| import com.jd.blockchain.transaction.ContractEventExecutor; | |||
| import com.jd.blockchain.transaction.ContractReturns.ReturnLongValue; | |||
| import com.jd.blockchain.transaction.ContractReturns.ReturnValue; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import org.junit.Before; | |||
| import org.junit.Test; | |||
| import static com.jd.blockchain.sdk.samples.SDKDemo_Constant.readChainCodes; | |||
| public class SDKDemo_Contract_Test { | |||
| private BlockchainKeypair adminKey; | |||
| private BlockchainKeypair adminKey; | |||
| private HashDigest ledgerHash; | |||
| private HashDigest ledgerHash; | |||
| private BlockchainService blockchainService; | |||
| private BlockchainService blockchainService; | |||
| @Before | |||
| public void init() { | |||
| // 生成连接网关的账号 | |||
| PrivKey privKey = KeyGenCommand.decodePrivKeyWithRawPassword(SDKDemo_Constant.PRIV_KEYS[0], SDKDemo_Constant.PASSWORD); | |||
| @Before | |||
| public void init() { | |||
| // 生成连接网关的账号 | |||
| PrivKey privKey = KeyGenCommand.decodePrivKeyWithRawPassword(SDKDemo_Constant.PRIV_KEYS[0], | |||
| SDKDemo_Constant.PASSWORD); | |||
| PubKey pubKey = KeyGenCommand.decodePubKey(SDKDemo_Constant.PUB_KEYS[0]); | |||
| PubKey pubKey = KeyGenCommand.decodePubKey(SDKDemo_Constant.PUB_KEYS[0]); | |||
| adminKey = new BlockchainKeypair(pubKey, privKey); | |||
| adminKey = new BlockchainKeypair(pubKey, privKey); | |||
| // 连接网关 | |||
| GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(SDKDemo_Constant.GW_IPADDR, | |||
| SDKDemo_Constant.GW_PORT, false, adminKey); | |||
| // 连接网关 | |||
| GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(SDKDemo_Constant.GW_IPADDR, | |||
| SDKDemo_Constant.GW_PORT, false, adminKey); | |||
| blockchainService = serviceFactory.getBlockchainService(); | |||
| blockchainService = serviceFactory.getBlockchainService(); | |||
| HashDigest[] ledgerHashs = blockchainService.getLedgerHashs(); | |||
| HashDigest[] ledgerHashs = blockchainService.getLedgerHashs(); | |||
| ledgerHash = ledgerHashs[0]; | |||
| } | |||
| ledgerHash = ledgerHashs[0]; | |||
| } | |||
| @Test | |||
| public void testContract() { | |||
| @Test | |||
| public void testContract() { | |||
| // 发布jar包 | |||
| // 定义交易; | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 发布jar包 | |||
| // 定义交易; | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| byte[] contractCode = readChainCodes("transfer.jar"); | |||
| // 生成一个合约账号 | |||
| BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | |||
| txTpl.contracts().deploy(contractDeployKey.getIdentity(), contractCode); | |||
| // 签名; | |||
| PreparedTransaction ptx = txTpl.prepare(); | |||
| ptx.sign(adminKey); | |||
| // 提交并等待共识返回; | |||
| TransactionResponse txResp = ptx.commit(); | |||
| System.out.println(txResp.isSuccess()); | |||
| // 首先注册一个数据账户 | |||
| BlockchainKeypair dataAccount = createDataAccount(); | |||
| String dataAddress = dataAccount.getAddress().toBase58(); | |||
| Bytes contractAddress = contractDeployKey.getAddress(); | |||
| // 创建两个账号: | |||
| String account0 = "jd_zhangsan", account1 = "jd_lisi"; | |||
| long account0Money = 3000L, account1Money = 2000L; | |||
| // 创建两个账户 | |||
| // 使用KV创建一个账户 | |||
| System.out.println(create(dataAddress, account0, account0Money, false, null)); | |||
| // 使用合约创建一个账户 | |||
| System.out.println(create(dataAddress, account1, account1Money, true, contractAddress)); | |||
| // 转账,使得双方钱达到一致 | |||
| System.out.println(transfer(dataAddress, account0, account1, 500L, contractAddress)); | |||
| // 读取当前结果 | |||
| System.out.println(read(dataAddress, account0, contractAddress)); | |||
| System.out.println(read(dataAddress, account1, contractAddress)); | |||
| // 读取账号历史信息 | |||
| System.out.println(readAll(dataAddress, account0, contractAddress)); | |||
| System.out.println(readAll(dataAddress, account1, contractAddress)); | |||
| } | |||
| private String readAll(String address, String account, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| EventResult<String> eventResult = txTpl.result((ContractEventExecutor<TransferContract>) () -> { | |||
| transferContract.readAll(address, account); | |||
| return transferContract; | |||
| }); | |||
| commit(txTpl); | |||
| return eventResult.get(); | |||
| } | |||
| private long read(String address, String account, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| EventResult<Long> eventResult = txTpl.result((ContractEventExecutor<TransferContract>) () -> { | |||
| transferContract.read(address, account); | |||
| return transferContract; | |||
| }); | |||
| commit(txTpl); | |||
| return eventResult.get(); | |||
| } | |||
| private String transfer(String address, String from, String to, long money, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| EventResult<String> eventResult = txTpl.result((ContractEventExecutor<TransferContract>) () -> { | |||
| transferContract.transfer(address, from, to, money); | |||
| return transferContract; | |||
| }); | |||
| commit(txTpl); | |||
| return eventResult.get(); | |||
| } | |||
| private BlockchainKeypair createDataAccount() { | |||
| // 首先注册一个数据账户 | |||
| BlockchainKeypair newDataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| txTpl.dataAccounts().register(newDataAccount.getIdentity()); | |||
| commit(txTpl); | |||
| return newDataAccount; | |||
| } | |||
| private String create(String address, String account, long money, boolean useContract, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| if (useContract) { | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| EventResult<String> eventResult = txTpl.result((ContractEventExecutor<TransferContract>) () -> { | |||
| transferContract.create(address, account, money); | |||
| return transferContract; | |||
| }); | |||
| commit(txTpl); | |||
| return eventResult.get(); | |||
| } else { | |||
| // 通过KV创建 | |||
| txTpl.dataAccount(address).setInt64(account, money, -1); | |||
| TransactionResponse txResp = commit(txTpl); | |||
| return account + money; | |||
| } | |||
| } | |||
| private TransactionResponse commit(TransactionTemplate txTpl) { | |||
| PreparedTransaction ptx = txTpl.prepare(); | |||
| ptx.sign(adminKey); | |||
| return ptx.commit(); | |||
| } | |||
| byte[] contractCode = readChainCodes("transfer.jar"); | |||
| // 生成一个合约账号 | |||
| BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | |||
| txTpl.contracts().deploy(contractDeployKey.getIdentity(), contractCode); | |||
| // 签名; | |||
| PreparedTransaction ptx = txTpl.prepare(); | |||
| ptx.sign(adminKey); | |||
| // 提交并等待共识返回; | |||
| TransactionResponse txResp = ptx.commit(); | |||
| System.out.println(txResp.isSuccess()); | |||
| // 首先注册一个数据账户 | |||
| BlockchainKeypair dataAccount = createDataAccount(); | |||
| String dataAddress = dataAccount.getAddress().toBase58(); | |||
| Bytes contractAddress = contractDeployKey.getAddress(); | |||
| // 创建两个账号: | |||
| String account0 = "jd_zhangsan", account1 = "jd_lisi"; | |||
| long account0Money = 3000L, account1Money = 2000L; | |||
| // 创建两个账户 | |||
| // 使用KV创建一个账户 | |||
| System.out.println(create(dataAddress, account0, account0Money, false, null)); | |||
| // 使用合约创建一个账户 | |||
| System.out.println(create(dataAddress, account1, account1Money, true, contractAddress)); | |||
| // 转账,使得双方钱达到一致 | |||
| System.out.println(transfer(dataAddress, account0, account1, 500L, contractAddress)); | |||
| // 读取当前结果 | |||
| System.out.println(read(dataAddress, account0, contractAddress)); | |||
| System.out.println(read(dataAddress, account1, contractAddress)); | |||
| // 读取账号历史信息 | |||
| System.out.println(readAll(dataAddress, account0, contractAddress)); | |||
| System.out.println(readAll(dataAddress, account1, contractAddress)); | |||
| } | |||
| private String readAll(String address, String account, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| ReturnValue<String> result = decode(transferContract.readAll(address, account)); | |||
| commit(txTpl); | |||
| return result.get(); | |||
| } | |||
| private long read(String address, String account, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| ReturnLongValue result = decode(transferContract.read(address, account)); | |||
| commit(txTpl); | |||
| return result.get(); | |||
| } | |||
| private String transfer(String address, String from, String to, long money, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| ReturnValue<String> result = decode(transferContract.transfer(address, from, to, money)); | |||
| commit(txTpl); | |||
| return result.get(); | |||
| } | |||
| private BlockchainKeypair createDataAccount() { | |||
| // 首先注册一个数据账户 | |||
| BlockchainKeypair newDataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| txTpl.dataAccounts().register(newDataAccount.getIdentity()); | |||
| commit(txTpl); | |||
| return newDataAccount; | |||
| } | |||
| private String create(String address, String account, long money, boolean useContract, Bytes contractAddress) { | |||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
| if (useContract) { | |||
| // 使用合约创建 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| ReturnValue<String> result = decode(transferContract.create(address, account, money)); | |||
| commit(txTpl); | |||
| return result.get(); | |||
| } else { | |||
| // 通过KV创建 | |||
| txTpl.dataAccount(address).setInt64(account, money, -1); | |||
| TransactionResponse txResp = commit(txTpl); | |||
| return account + money; | |||
| } | |||
| } | |||
| private TransactionResponse commit(TransactionTemplate txTpl) { | |||
| PreparedTransaction ptx = txTpl.prepare(); | |||
| ptx.sign(adminKey); | |||
| return ptx.commit(); | |||
| } | |||
| } | |||
| @@ -24,24 +24,35 @@ import java.util.Random; | |||
| import java.util.concurrent.CountDownLatch; | |||
| import java.util.concurrent.atomic.AtomicLong; | |||
| import com.jd.blockchain.contract.ContractSerializeUtils; | |||
| import com.jd.blockchain.contract.EventResult; | |||
| import com.jd.blockchain.contract.ReadContract; | |||
| import com.jd.blockchain.ledger.*; | |||
| import com.jd.blockchain.transaction.ContractEventExecutor; | |||
| import org.apache.commons.io.FileUtils; | |||
| import org.springframework.core.io.ClassPathResource; | |||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
| import com.jd.blockchain.contract.ContractSerializeUtils; | |||
| import com.jd.blockchain.contract.EventResult; | |||
| import com.jd.blockchain.contract.ReadContract; | |||
| import com.jd.blockchain.crypto.AddressEncoding; | |||
| import com.jd.blockchain.crypto.AsymmetricKeypair; | |||
| import com.jd.blockchain.crypto.HashDigest; | |||
| import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||
| import com.jd.blockchain.ledger.BlockchainKeypair; | |||
| import com.jd.blockchain.ledger.KVDataEntry; | |||
| import com.jd.blockchain.ledger.LedgerBlock; | |||
| import com.jd.blockchain.ledger.LedgerInitOperation; | |||
| import com.jd.blockchain.ledger.OperationResult; | |||
| import com.jd.blockchain.ledger.PreparedTransaction; | |||
| import com.jd.blockchain.ledger.TransactionResponse; | |||
| import com.jd.blockchain.ledger.TransactionState; | |||
| import com.jd.blockchain.ledger.TransactionTemplate; | |||
| import com.jd.blockchain.ledger.UserRegisterOperation; | |||
| import com.jd.blockchain.ledger.core.LedgerRepository; | |||
| import com.jd.blockchain.ledger.core.impl.LedgerManager; | |||
| import com.jd.blockchain.sdk.BlockchainService; | |||
| import com.jd.blockchain.storage.service.DbConnection; | |||
| import com.jd.blockchain.storage.service.DbConnectionFactory; | |||
| import com.jd.blockchain.tools.initializer.LedgerBindingConfig; | |||
| import com.jd.blockchain.transaction.ContractEventExecutor; | |||
| import static com.jd.blockchain.transaction.ContractReturns.*; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import com.jd.blockchain.utils.concurrent.ThreadInvoker; | |||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||
| @@ -576,10 +587,7 @@ public class IntegrationBase { | |||
| ReadContract readContract1 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | |||
| EventResult<String> read1 = txContract.result((ContractEventExecutor<ReadContract>) () -> { | |||
| readContract1.read(newDataAccount.getAddress().toBase58(), key1); | |||
| return readContract1; | |||
| }); | |||
| ReturnValue<String> result1 = decode(readContract1.read(newDataAccount.getAddress().toBase58(), key1)); | |||
| ReadContract readContract2 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | |||
| @@ -587,10 +595,7 @@ public class IntegrationBase { | |||
| ReadContract readContract3 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | |||
| EventResult<Long> read3 = txContract.result((ContractEventExecutor<ReadContract>) () -> { | |||
| readContract3.readVersion(newDataAccount.getAddress().toBase58(), key2); | |||
| return readContract3; | |||
| }); | |||
| ReturnValue<Long> result3 = decode(readContract3.readVersion(newDataAccount.getAddress().toBase58(), key2)); | |||
| // 签名; | |||
| PreparedTransaction contractPtx = txContract.prepare(); | |||
| @@ -602,8 +607,8 @@ public class IntegrationBase { | |||
| OperationResult[] operationResults = readTxResp.getOperationResults(); | |||
| // 通过EventResult获取结果 | |||
| System.out.printf("readContract1.result = %s \r\n", read1.get()); | |||
| System.out.printf("readContract3.result = %s \r\n", read3.get()); | |||
| System.out.printf("readContract1.result = %s \r\n", result1.get()); | |||
| System.out.printf("readContract3.result = %s \r\n", result3.get()); | |||
| // 打印结果 | |||
| @@ -11,6 +11,7 @@ import com.jd.blockchain.contract.EventProcessingAware; | |||
| import com.jd.blockchain.contract.LedgerContext; | |||
| import com.jd.blockchain.crypto.HashDigest; | |||
| import com.jd.blockchain.ledger.BlockchainIdentity; | |||
| import com.jd.blockchain.ledger.BytesValue; | |||
| import com.jd.blockchain.ledger.ContractEventSendOperation; | |||
| import com.jd.blockchain.ledger.Operation; | |||
| import com.jd.blockchain.ledger.TransactionRequest; | |||
| @@ -33,7 +34,7 @@ public class MockerContractExeHandle implements OperationHandle { | |||
| private HashDigest ledgerHash; | |||
| @Override | |||
| public byte[] process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||
| public BytesValue process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||
| LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { | |||
| ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | |||
| @@ -76,11 +77,6 @@ public class MockerContractExeHandle implements OperationHandle { | |||
| return ContractSerializeUtils.serialize(result); | |||
| } | |||
| // @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 ContractEventSendOperation.class.isAssignableFrom(operationType); | |||