| @@ -9,6 +9,8 @@ package com.jd.blockchain.consts; | |||||
| public interface DataCodes { | public interface DataCodes { | ||||
| public static final int BYTES_VALUE = 0x80; | public static final int BYTES_VALUE = 0x80; | ||||
| public static final int BYTES_VALUE_LIST = 0x81; | |||||
| public static final int BLOCK_CHAIN_IDENTITY = 0x90; | public static final int BLOCK_CHAIN_IDENTITY = 0x90; | ||||
| @@ -16,8 +18,6 @@ public interface DataCodes { | |||||
| public static final int BLOCK_BODY = 0x110; | public static final int BLOCK_BODY = 0x110; | ||||
| // public static final int BLOCK_LEDGER = 0x110; | |||||
| public static final int BLOCK_GENESIS = 0x120; | public static final int BLOCK_GENESIS = 0x120; | ||||
| public static final int DATA_SNAPSHOT = 0x130; | public static final int DATA_SNAPSHOT = 0x130; | ||||
| @@ -1,16 +1,14 @@ | |||||
| package com.jd.blockchain.contract.engine; | package com.jd.blockchain.contract.engine; | ||||
| import com.jd.blockchain.contract.ContractEventContext; | import com.jd.blockchain.contract.ContractEventContext; | ||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| import java.util.concurrent.CompletableFuture; | |||||
| import java.util.concurrent.Future; | |||||
| public interface ContractCode { | public interface ContractCode { | ||||
| Bytes getAddress(); | Bytes getAddress(); | ||||
| long getVersion(); | long getVersion(); | ||||
| 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.ContractException; | ||||
| import com.jd.blockchain.contract.EventProcessingAware; | import com.jd.blockchain.contract.EventProcessingAware; | ||||
| import com.jd.blockchain.contract.engine.ContractCode; | import com.jd.blockchain.contract.engine.ContractCode; | ||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| /** | /** | ||||
| @@ -44,7 +45,7 @@ public abstract class AbstractContractCode implements ContractCode { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public byte[] processEvent(ContractEventContext eventContext) { | |||||
| public BytesValue processEvent(ContractEventContext eventContext) { | |||||
| EventProcessingAware evtProcAwire = null; | EventProcessingAware evtProcAwire = null; | ||||
| Object retn = null; | Object retn = null; | ||||
| Exception error = null; | Exception error = null; | ||||
| @@ -90,13 +91,13 @@ public abstract class AbstractContractCode implements ContractCode { | |||||
| eventContext.getEvent(), address.toString(), error.getMessage()), error); | eventContext.getEvent(), address.toString(), error.getMessage()), error); | ||||
| } | } | ||||
| byte[] retnBytes = resolveResult(retn); | |||||
| BytesValue retnBytes = resolveResult(retn); | |||||
| return retnBytes; | return retnBytes; | ||||
| } | } | ||||
| protected abstract Object getContractInstance(); | protected abstract Object getContractInstance(); | ||||
| private byte[] resolveResult(Object retn) { | |||||
| private BytesValue resolveResult(Object retn) { | |||||
| if (retn == null) { | if (retn == null) { | ||||
| return null; | return null; | ||||
| } | } | ||||
| @@ -9,6 +9,7 @@ import com.jd.blockchain.contract.Contract; | |||||
| import com.jd.blockchain.contract.ContractEventContext; | import com.jd.blockchain.contract.ContractEventContext; | ||||
| import com.jd.blockchain.contract.ContractException; | import com.jd.blockchain.contract.ContractException; | ||||
| import com.jd.blockchain.contract.ContractType; | import com.jd.blockchain.contract.ContractType; | ||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.runtime.Module; | import com.jd.blockchain.runtime.Module; | ||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| @@ -65,7 +66,7 @@ public class JavaContractCode extends AbstractContractCode { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public byte[] processEvent(ContractEventContext eventContext) { | |||||
| public BytesValue processEvent(ContractEventContext eventContext) { | |||||
| if (LOGGER.isDebugEnabled()) { | if (LOGGER.isDebugEnabled()) { | ||||
| LOGGER.debug("Start processing event[%s] of contract[%s]...", eventContext.getEvent(), address.toString()); | 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; | private ContractEventContext eventContext; | ||||
| public ContractExecution(ContractEventContext contractEventContext) { | public ContractExecution(ContractEventContext contractEventContext) { | ||||
| @@ -99,7 +100,7 @@ public class JavaContractCode extends AbstractContractCode { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public byte[] call() throws Exception { | |||||
| public BytesValue call() throws Exception { | |||||
| return JavaContractCode.super.processEvent(eventContext); | return JavaContractCode.super.processEvent(eventContext); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,8 +1,8 @@ | |||||
| package com.jd.blockchain.ledger.core; | package com.jd.blockchain.ledger.core; | ||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
| import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
| import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
| public interface OperationHandle { | public interface OperationHandle { | ||||
| @@ -30,7 +30,7 @@ public interface OperationHandle { | |||||
| * | * | ||||
| * @return 操作执行结果 | * @return 操作执行结果 | ||||
| */ | */ | ||||
| byte[] process(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, | |||||
| BytesValue process(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, | |||||
| LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService); | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService); | ||||
| // /** | // /** | ||||
| @@ -8,7 +8,7 @@ import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.ledger.ContractEventSendOperation; | |||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.ledger.LedgerBlock; | import com.jd.blockchain.ledger.LedgerBlock; | ||||
| import com.jd.blockchain.ledger.LedgerException; | import com.jd.blockchain.ledger.LedgerException; | ||||
| import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
| @@ -110,7 +110,7 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| int opIndex = 0; | int opIndex = 0; | ||||
| for (Operation op : ops) { | for (Operation op : ops) { | ||||
| opHandle = opHandles.getHandle(op.getClass()); | opHandle = opHandles.getHandle(op.getClass()); | ||||
| byte[] opResult = opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | |||||
| BytesValue opResult = opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | |||||
| if (opResult != null) { | if (opResult != null) { | ||||
| operationResults.add(new OperationResultData(opIndex, opResult)); | operationResults.add(new OperationResultData(opIndex, opResult)); | ||||
| } | } | ||||
| @@ -1,13 +1,10 @@ | |||||
| package com.jd.blockchain.ledger.core.impl.handles; | package com.jd.blockchain.ledger.core.impl.handles; | ||||
| import static com.jd.blockchain.utils.BaseConstant.CONTRACT_SERVICE_PROVIDER; | |||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
| import com.jd.blockchain.contract.LocalContractEventContext; | import com.jd.blockchain.contract.LocalContractEventContext; | ||||
| import com.jd.blockchain.contract.engine.ContractCode; | import com.jd.blockchain.contract.engine.ContractCode; | ||||
| import com.jd.blockchain.contract.engine.ContractEngine; | |||||
| import com.jd.blockchain.contract.engine.ContractServiceProviders; | |||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.ledger.ContractEventSendOperation; | import com.jd.blockchain.ledger.ContractEventSendOperation; | ||||
| import com.jd.blockchain.ledger.LedgerException; | import com.jd.blockchain.ledger.LedgerException; | ||||
| import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
| @@ -29,7 +26,7 @@ public abstract class AbtractContractEventHandle implements OperationHandle { | |||||
| } | } | ||||
| @Override | @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) { | LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { | ||||
| ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | ||||
| // 先从账本校验合约的有效性; | // 先从账本校验合约的有效性; | ||||
| @@ -1,6 +1,8 @@ | |||||
| package com.jd.blockchain.ledger.core.impl.handles; | package com.jd.blockchain.ledger.core.impl.handles; | ||||
| import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
| import org.springframework.stereotype.Service; | |||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.ledger.ContractCodeDeployOperation; | import com.jd.blockchain.ledger.ContractCodeDeployOperation; | ||||
| import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
| import com.jd.blockchain.ledger.core.LedgerDataSet; | import com.jd.blockchain.ledger.core.LedgerDataSet; | ||||
| @@ -9,17 +11,11 @@ import com.jd.blockchain.ledger.core.OperationHandle; | |||||
| import com.jd.blockchain.ledger.core.TransactionRequestContext; | import com.jd.blockchain.ledger.core.TransactionRequestContext; | ||||
| import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
| import com.jd.blockchain.utils.Bytes; | |||||
| import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
| import org.springframework.stereotype.Service; | |||||
| import java.util.concurrent.Future; | |||||
| @Service | @Service | ||||
| public class ContractCodeDeployOperationHandle implements OperationHandle { | public class ContractCodeDeployOperationHandle implements OperationHandle { | ||||
| @Override | @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) { | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | ||||
| ContractCodeDeployOperation contractOP = (ContractCodeDeployOperation) op; | ContractCodeDeployOperation contractOP = (ContractCodeDeployOperation) op; | ||||
| // TODO: 校验合约代码的正确性; | // TODO: 校验合约代码的正确性; | ||||
| @@ -1,7 +1,5 @@ | |||||
| package com.jd.blockchain.ledger.core.impl.handles; | 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 org.springframework.stereotype.Service; | ||||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | import com.jd.blockchain.binaryproto.DataContractRegistry; | ||||
| @@ -24,7 +22,7 @@ public class DataAccountKVSetOperationHandle implements OperationHandle { | |||||
| } | } | ||||
| @Override | @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) { | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | ||||
| DataAccountKVSetOperation kvWriteOp = (DataAccountKVSetOperation) op; | DataAccountKVSetOperation kvWriteOp = (DataAccountKVSetOperation) op; | ||||
| DataAccount account = dataset.getDataAccountSet().getDataAccount(kvWriteOp.getAccountAddress()); | DataAccount account = dataset.getDataAccountSet().getDataAccount(kvWriteOp.getAccountAddress()); | ||||
| @@ -1,6 +1,9 @@ | |||||
| package com.jd.blockchain.ledger.core.impl.handles; | package com.jd.blockchain.ledger.core.impl.handles; | ||||
| import org.springframework.stereotype.Service; | |||||
| import com.jd.blockchain.ledger.BlockchainIdentity; | import com.jd.blockchain.ledger.BlockchainIdentity; | ||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.ledger.DataAccountRegisterOperation; | import com.jd.blockchain.ledger.DataAccountRegisterOperation; | ||||
| import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
| import com.jd.blockchain.ledger.core.LedgerDataSet; | import com.jd.blockchain.ledger.core.LedgerDataSet; | ||||
| @@ -9,14 +12,11 @@ import com.jd.blockchain.ledger.core.OperationHandle; | |||||
| import com.jd.blockchain.ledger.core.TransactionRequestContext; | import com.jd.blockchain.ledger.core.TransactionRequestContext; | ||||
| import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
| import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
| import org.springframework.stereotype.Service; | |||||
| @Service | @Service | ||||
| public class DataAccountRegisterOperationHandle implements OperationHandle { | public class DataAccountRegisterOperationHandle implements OperationHandle { | ||||
| @Override | @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) { | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | ||||
| DataAccountRegisterOperation dataAccountRegOp = (DataAccountRegisterOperation) op; | DataAccountRegisterOperation dataAccountRegOp = (DataAccountRegisterOperation) op; | ||||
| BlockchainIdentity bid = dataAccountRegOp.getAccountID(); | BlockchainIdentity bid = dataAccountRegOp.getAccountID(); | ||||
| @@ -1,6 +1,7 @@ | |||||
| package com.jd.blockchain.ledger.core.impl.handles; | package com.jd.blockchain.ledger.core.impl.handles; | ||||
| import com.jd.blockchain.ledger.BlockchainIdentity; | import com.jd.blockchain.ledger.BlockchainIdentity; | ||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
| import com.jd.blockchain.ledger.UserRegisterOperation; | import com.jd.blockchain.ledger.UserRegisterOperation; | ||||
| import com.jd.blockchain.ledger.core.LedgerDataSet; | import com.jd.blockchain.ledger.core.LedgerDataSet; | ||||
| @@ -9,13 +10,12 @@ import com.jd.blockchain.ledger.core.OperationHandle; | |||||
| import com.jd.blockchain.ledger.core.TransactionRequestContext; | import com.jd.blockchain.ledger.core.TransactionRequestContext; | ||||
| import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
| public class UserRegisterOperationHandle implements OperationHandle { | public class UserRegisterOperationHandle implements OperationHandle { | ||||
| @Override | @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) { | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | ||||
| @@ -26,14 +26,9 @@ public class UserRegisterOperationHandle implements OperationHandle { | |||||
| dataset.getUserAccountSet().register(userAddress, bid.getPubKey()); | 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 | @Override | ||||
| public boolean support(Class<?> operationType) { | public boolean support(Class<?> operationType) { | ||||
| return UserRegisterOperation.class.isAssignableFrom(operationType); | return UserRegisterOperation.class.isAssignableFrom(operationType); | ||||
| @@ -120,8 +120,9 @@ public class ContractInvokingTest { | |||||
| assertEquals(1, opResults.length); | assertEquals(1, opResults.length); | ||||
| assertEquals(0, opResults[0].getIndex()); | 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(); | TransactionBatchResultHandle txResultHandle = txbatchProcessor.prepare(); | ||||
| @@ -135,7 +136,10 @@ public class ContractInvokingTest { | |||||
| // 再验证一次结果; | // 再验证一次结果; | ||||
| assertEquals(1, opResults.length); | assertEquals(1, opResults.length); | ||||
| assertEquals(0, opResults[0].getIndex()); | 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) { | private HashDigest initLedger(MemoryKVStorage storage, BlockchainKeypair... partiKeys) { | ||||
| @@ -1,157 +1,164 @@ | |||||
| package com.jd.blockchain.contract; | 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.ArrayList; | ||||
| import java.util.HashMap; | import java.util.HashMap; | ||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.Map; | 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 { | 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 | * @return | ||||
| */ | */ | ||||
| public static ContractType resolve(Class<?> contractIntf) { | public static ContractType resolve(Class<?> contractIntf) { | ||||
| // TODO:方法会检查合约方法声明的类型和返回值类型; | |||||
| // 如果是Class则首先获取其接口 | // 如果是Class则首先获取其接口 | ||||
| if (!contractIntf.isInterface()) { | if (!contractIntf.isInterface()) { | ||||
| Class<?> realIntf = null; | Class<?> realIntf = null; | ||||
| Class<?>[] interfaces = contractIntf.getInterfaces(); | Class<?>[] interfaces = contractIntf.getInterfaces(); | ||||
| for (Class<?> intf: interfaces) { | |||||
| for (Class<?> intf : interfaces) { | |||||
| if (intf.isAnnotationPresent(Contract.class)) { | if (intf.isAnnotationPresent(Contract.class)) { | ||||
| realIntf = intf; | realIntf = intf; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (realIntf == null) { | 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; | contractIntf = realIntf; | ||||
| } | } | ||||
| @@ -105,22 +105,25 @@ public class ContractType { | |||||
| if (contractEvent != null) { | if (contractEvent != null) { | ||||
| String eventName = contractEvent.name(); | 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."); | 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(); | Class<?>[] paramTypes = method.getParameterTypes(); | ||||
| for(Class<?> currParamType : paramTypes) { | |||||
| for (Class<?> currParamType : paramTypes) { | |||||
| if (!ContractSerializeUtils.support(currParamType)) { | 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(); | Class<?> returnType = method.getReturnType(); | ||||
| if (!ContractSerializeUtils.support(returnType)) { | 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); | contractType.events.put(eventName, method); | ||||
| @@ -132,10 +135,7 @@ public class ContractType { | |||||
| @Override | @Override | ||||
| public String toString() { | 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) | @DataField(order = 4, primitiveType = PrimitiveType.BYTES) | ||||
| byte[] getArgs(); | byte[] getArgs(); | ||||
| /** | |||||
| * 获得交易操作时间; | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @DataField(order = 5, primitiveType = PrimitiveType.INT64) | |||||
| long getTxOpTime(); | |||||
| } | } | ||||
| @@ -36,24 +36,22 @@ public enum DataType { | |||||
| INT32(PrimitiveType.INT32.CODE), | INT32(PrimitiveType.INT32.CODE), | ||||
| INT64(PrimitiveType.INT64.CODE), | INT64(PrimitiveType.INT64.CODE), | ||||
| /** | /** | ||||
| * 文本数据; | * 文本数据; | ||||
| */ | */ | ||||
| TEXT(PrimitiveType.TEXT.CODE), | TEXT(PrimitiveType.TEXT.CODE), | ||||
| /** | /** | ||||
| * 二进制数据; | * 二进制数据; | ||||
| */ | */ | ||||
| BYTES(PrimitiveType.BYTES.CODE), | BYTES(PrimitiveType.BYTES.CODE), | ||||
| /** | /** | ||||
| * 时间戳; | * 时间戳; | ||||
| */ | */ | ||||
| TIMESTAMP((byte) (BaseType.INTEGER | 0x08)), | TIMESTAMP((byte) (BaseType.INTEGER | 0x08)), | ||||
| /** | /** | ||||
| * 文本数据; | * 文本数据; | ||||
| */ | */ | ||||
| @@ -64,7 +62,6 @@ public enum DataType { | |||||
| */ | */ | ||||
| XML((byte) (BaseType.TEXT | 0x02)), | XML((byte) (BaseType.TEXT | 0x02)), | ||||
| /** | /** | ||||
| * 大整数; | * 大整数; | ||||
| */ | */ | ||||
| @@ -84,28 +81,32 @@ public enum DataType { | |||||
| * 位置坐标; | * 位置坐标; | ||||
| */ | */ | ||||
| LOCATION((byte) (BaseType.BYTES | 0x04)), | LOCATION((byte) (BaseType.BYTES | 0x04)), | ||||
| /** | /** | ||||
| * 公钥; | * 公钥; | ||||
| */ | */ | ||||
| PUB_KEY((byte) (BaseType.BYTES | 0x05)), | PUB_KEY((byte) (BaseType.BYTES | 0x05)), | ||||
| /** | /** | ||||
| * 签名摘要; | * 签名摘要; | ||||
| */ | */ | ||||
| SIGNATURE_DIGEST((byte) (BaseType.BYTES | 0x06)), | SIGNATURE_DIGEST((byte) (BaseType.BYTES | 0x06)), | ||||
| /** | /** | ||||
| * 哈希摘要; | * 哈希摘要; | ||||
| */ | */ | ||||
| HASH_DIGEST((byte) (BaseType.BYTES | 0x07)), | 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) | @EnumField(type = PrimitiveType.INT8) | ||||
| public final byte CODE; | public final byte CODE; | ||||
| @@ -8,9 +8,9 @@ import com.jd.blockchain.consts.DataCodes; | |||||
| @DataContract(code = DataCodes.TX_OP_RESULT) | @DataContract(code = DataCodes.TX_OP_RESULT) | ||||
| public interface OperationResult { | 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 int index; | ||||
| private byte[] result; | |||||
| private BytesValue result; | |||||
| public OperationResultData() { | public OperationResultData() { | ||||
| } | } | ||||
| public OperationResultData(int index, byte[] result) { | |||||
| public OperationResultData(int index, BytesValue result) { | |||||
| this.index = index; | this.index = index; | ||||
| this.result = result; | this.result = result; | ||||
| } | } | ||||
| @@ -21,7 +21,7 @@ public class OperationResultData implements OperationResult { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public byte[] getResult() { | |||||
| public BytesValue getResult() { | |||||
| return result; | return result; | ||||
| } | } | ||||
| @@ -29,7 +29,7 @@ public class OperationResultData implements OperationResult { | |||||
| this.index = index; | this.index = index; | ||||
| } | } | ||||
| public void setResult(byte[] result) { | |||||
| public void setResult(BytesValue result) { | |||||
| this.result = result; | this.result = result; | ||||
| } | } | ||||
| } | } | ||||
| @@ -2,7 +2,6 @@ package com.jd.blockchain.ledger; | |||||
| import com.jd.blockchain.crypto.AsymmetricKeypair; | import com.jd.blockchain.crypto.AsymmetricKeypair; | ||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.crypto.SignatureFunction; | |||||
| /** | /** | ||||
| * 已就绪的交易; | * 已就绪的交易; | ||||
| @@ -10,7 +9,7 @@ import com.jd.blockchain.crypto.SignatureFunction; | |||||
| * @author huanghaiquan | * @author huanghaiquan | ||||
| * | * | ||||
| */ | */ | ||||
| public interface PreparedTransaction extends HashObject { | |||||
| public interface PreparedTransaction extends HashObject { | |||||
| /** | /** | ||||
| * 交易内容的 Hash; | * 交易内容的 Hash; | ||||
| @@ -33,10 +32,8 @@ public interface PreparedTransaction extends HashObject { | |||||
| /** | /** | ||||
| * 对交易进行签名; | * 对交易进行签名; | ||||
| * | * | ||||
| * @param address | |||||
| * 签名账户的地址; | |||||
| * @param privKey | |||||
| * 签名账户的私钥; | |||||
| * @param address 签名账户的地址; | |||||
| * @param privKey 签名账户的私钥; | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| DigitalSignature sign(AsymmetricKeypair keyPair); | DigitalSignature sign(AsymmetricKeypair keyPair); | ||||
| @@ -44,17 +41,22 @@ public interface PreparedTransaction extends HashObject { | |||||
| /** | /** | ||||
| * 加入签名; | * 加入签名; | ||||
| * | * | ||||
| * @param address | |||||
| * 签名账户的地址; | |||||
| * @param digest | |||||
| * Base64格式的签名摘要; | |||||
| * @param address 签名账户的地址; | |||||
| * @param digest Base64格式的签名摘要; | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| void addSignature(DigitalSignature signature); | void addSignature(DigitalSignature signature); | ||||
| /** | /** | ||||
| * 生成交易请求; | |||||
| * 提交交易请求到共识节点;<br> | |||||
| * | |||||
| * 这是同步方法,将阻塞当前线程,直到交易处理完成并返回结果之后,此方法才返回给调用者; | |||||
| * | * | ||||
| */ | */ | ||||
| TransactionResponse commit(); | TransactionResponse commit(); | ||||
| /** | |||||
| * 取消交易;<br> | |||||
| */ | |||||
| void cancel(); | |||||
| } | } | ||||
| @@ -4,8 +4,8 @@ import java.util.ArrayList; | |||||
| import java.util.Collection; | import java.util.Collection; | ||||
| import java.util.List; | import java.util.List; | ||||
| import com.jd.blockchain.contract.EventResult; | |||||
| import com.jd.blockchain.ledger.BlockchainIdentity; | import com.jd.blockchain.ledger.BlockchainIdentity; | ||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.ledger.ContractCodeDeployOperation; | import com.jd.blockchain.ledger.ContractCodeDeployOperation; | ||||
| import com.jd.blockchain.ledger.ContractEventSendOperation; | import com.jd.blockchain.ledger.ContractEventSendOperation; | ||||
| import com.jd.blockchain.ledger.DataAccountKVSetOperation; | import com.jd.blockchain.ledger.DataAccountKVSetOperation; | ||||
| @@ -44,6 +44,7 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
| private ContractInvocationProxyBuilder contractInvoProxyBuilder = new ContractInvocationProxyBuilder(); | private ContractInvocationProxyBuilder contractInvoProxyBuilder = new ContractInvocationProxyBuilder(); | ||||
| // TODO: 暂时只支持单线程情形,未考虑多线程; | |||||
| private List<Operation> operationList = new ArrayList<>(); | private List<Operation> operationList = new ArrayList<>(); | ||||
| @Override | @Override | ||||
| @@ -90,16 +91,43 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
| return contractInvoProxyBuilder.create(address, contractIntf, contractEventSendOpBuilder); | return contractInvoProxyBuilder.create(address, contractIntf, contractEventSendOpBuilder); | ||||
| } | } | ||||
| @Override | |||||
| public <T> EventResult<T> result(ContractEventExecutor execute) { | |||||
| return contractInvoProxyBuilder.execute(execute); | |||||
| } | |||||
| /** | |||||
| * 返回已经定义的操作列表; | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| public Collection<Operation> getOperations() { | public Collection<Operation> getOperations() { | ||||
| // TODO: 合并操作列表中可能的重复操作; | |||||
| return operationList; | 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() { | public void clear() { | ||||
| operationList.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 | @Override | ||||
| public DataAccountKVSetOperationBuilder setText(String key, String value, long expVersion) { | public DataAccountKVSetOperationBuilder setText(String key, String value, long expVersion) { | ||||
| innerBuilder.setText(key, value, expVersion); | innerBuilder.setText(key, value, expVersion); | ||||
| @@ -202,13 +223,6 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
| return this; | return this; | ||||
| } | } | ||||
| // @Override | |||||
| // public DataAccountKVSetOperationBuilder set(String key, String value, long expVersion) { | |||||
| // innerBuilder.setText(key, value, expVersion); | |||||
| // addOperation(); | |||||
| // return this; | |||||
| // } | |||||
| @Override | @Override | ||||
| public DataAccountKVSetOperationBuilder setJSON(String key, String value, long expVersion) { | public DataAccountKVSetOperationBuilder setJSON(String key, String value, long expVersion) { | ||||
| innerBuilder.setJSON(key, value, expVersion); | innerBuilder.setJSON(key, value, expVersion); | ||||
| @@ -233,7 +247,6 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
| } | } | ||||
| private class ContractCodeDeployOperationBuilderFilter implements ContractCodeDeployOperationBuilder { | private class ContractCodeDeployOperationBuilderFilter implements ContractCodeDeployOperationBuilder { | ||||
| @Override | @Override | ||||
| public ContractCodeDeployOperation deploy(BlockchainIdentity id, byte[] chainCode) { | public ContractCodeDeployOperation deploy(BlockchainIdentity id, byte[] chainCode) { | ||||
| ContractCodeDeployOperation op = CONTRACT_CODE_DEPLOY_OP_BUILDER.deploy(id, chainCode); | ContractCodeDeployOperation op = CONTRACT_CODE_DEPLOY_OP_BUILDER.deploy(id, chainCode); | ||||
| @@ -250,25 +263,38 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
| } | } | ||||
| @Override | @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); | operationList.add(op); | ||||
| return op; | return op; | ||||
| } | } | ||||
| } | |||||
| /** | |||||
| * 不做任何操作的返回值处理器; | |||||
| * | |||||
| * @author huanghaiquan | |||||
| * | |||||
| */ | |||||
| private static class NullOperationReturnValueHandler implements OperationReturnValueHandler { | |||||
| private int operationIndex; | |||||
| public NullOperationReturnValueHandler(int operationIndex) { | |||||
| this.operationIndex = operationIndex; | |||||
| } | |||||
| @Override | @Override | ||||
| public ContractEventSendOperation send(String address) { | |||||
| return send(Bytes.fromBase58(address)); | |||||
| public int getOperationIndex() { | |||||
| return operationIndex; | |||||
| } | } | ||||
| @Override | @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 Bytes contractAddress; | ||||
| private byte[] args; | private byte[] args; | ||||
| private String event; | 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) { | 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.contractAddress = contractAddress; | ||||
| this.event = event; | this.event = event; | ||||
| this.args = args; | 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 | @Override | ||||
| @@ -71,17 +37,12 @@ public class ContractEventSendOpTemplate implements ContractEventSendOperation { | |||||
| return args; | 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 address 合约地址; | ||||
| * @param event 事件名; | |||||
| * @param args 事件参数; | |||||
| * @param event 事件名; | |||||
| * @param args 事件参数; | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| @Deprecated | |||||
| ContractEventSendOperation send(String address, String event, byte[] args); | ContractEventSendOperation send(String address, String event, byte[] args); | ||||
| /** | /** | ||||
| * @param address 合约地址; | * @param address 合约地址; | ||||
| * @param event 事件名; | |||||
| * @param args 事件参数; | |||||
| * @param event 事件名; | |||||
| * @param args 事件参数; | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| @Deprecated | |||||
| ContractEventSendOperation send(Bytes address, String event, byte[] args); | 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.Bytes; | ||||
| import com.jd.blockchain.utils.IllegalDataException; | import com.jd.blockchain.utils.IllegalDataException; | ||||
| /** | |||||
| * 合约调用代理的构建器; | |||||
| * | |||||
| * @author huanghaiquan | |||||
| * | |||||
| */ | |||||
| public class ContractInvocationProxyBuilder { | public class ContractInvocationProxyBuilder { | ||||
| private Map<Class<?>, ContractType> contractTypes = new ConcurrentHashMap<>(); | 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) { | public <T> T create(String address, Class<T> contractIntf, ContractEventSendOperationBuilder contractEventBuilder) { | ||||
| return create(Bytes.fromBase58(address), contractIntf, 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) { | public <T> T create(Bytes address, Class<T> contractIntf, ContractEventSendOperationBuilder contractEventBuilder) { | ||||
| ContractType contractType = resolveContractType(contractIntf); | 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(), | T proxy = (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), | ||||
| new Class<?>[] { contractIntf }, proxyHandler); | new Class<?>[] { contractIntf }, proxyHandler); | ||||
| // 创建关联关系 | |||||
| contractOperations.put(proxy, proxyHandler.opIndex()); | |||||
| // // 创建关联关系 | |||||
| // contractOperations.put(proxy, proxyHandler.opIndex()); | |||||
| return proxy; | 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) { | private ContractType resolveContractType(Class<?> contractIntf) { | ||||
| ContractType contractType = contractTypes.get(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; | package com.jd.blockchain.transaction; | ||||
| import com.jd.blockchain.contract.EventResult; | |||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| public interface EventOperator { | public interface EventOperator { | ||||
| // /** | |||||
| // * 合约事件; | |||||
| // * | |||||
| // * @return | |||||
| // */ | |||||
| // @Deprecated | |||||
| // ContractEventSendOperationBuilder contractEvents(); | |||||
| /** | /** | ||||
| * 创建调用合约的代理实例; | * 创建调用合约的代理实例; | ||||
| * | * | ||||
| @@ -31,11 +22,11 @@ public interface EventOperator { | |||||
| */ | */ | ||||
| <T> T contract(Bytes address, Class<T> contractIntf); | <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; | 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.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.AsymmetricKeypair; | ||||
| import com.jd.blockchain.crypto.Crypto; | import com.jd.blockchain.crypto.Crypto; | ||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.crypto.PrivKey; | import com.jd.blockchain.crypto.PrivKey; | ||||
| import com.jd.blockchain.crypto.SignatureDigest; | import com.jd.blockchain.crypto.SignatureDigest; | ||||
| import com.jd.blockchain.crypto.SignatureFunction; | 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 { | public class PreparedTx implements PreparedTransaction { | ||||
| @@ -19,16 +25,29 @@ public class PreparedTx implements PreparedTransaction { | |||||
| private TransactionService txProcessor; | 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.txReqBuilder = txReqBuilder; | ||||
| this.txProcessor = txProcessor; | 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 | @Override | ||||
| @@ -59,23 +78,32 @@ public class PreparedTx implements PreparedTransaction { | |||||
| @Override | @Override | ||||
| public TransactionResponse commit() { | 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; | package com.jd.blockchain.transaction; | ||||
| import java.util.Collection; | |||||
| import com.jd.blockchain.binaryproto.BinaryProtocol; | import com.jd.blockchain.binaryproto.BinaryProtocol; | ||||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | import com.jd.blockchain.binaryproto.DataContractRegistry; | ||||
| import com.jd.blockchain.contract.EventResult; | |||||
| import com.jd.blockchain.crypto.Crypto; | import com.jd.blockchain.crypto.Crypto; | ||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.ledger.TransactionBuilder; | 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.ledger.TransactionRequestBuilder; | ||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| public class TxBuilder implements TransactionBuilder { | public class TxBuilder implements TransactionBuilder { | ||||
| static { | static { | ||||
| @@ -22,8 +20,6 @@ public class TxBuilder implements TransactionBuilder { | |||||
| private BlockchainOperationFactory opFactory = new BlockchainOperationFactory(); | private BlockchainOperationFactory opFactory = new BlockchainOperationFactory(); | ||||
| private Map<Object, Integer> contractProxyMap = new HashMap<>(); | |||||
| private static final String DEFAULT_HASH_ALGORITHM = "SHA256"; | private static final String DEFAULT_HASH_ALGORITHM = "SHA256"; | ||||
| private HashDigest ledgerHash; | private HashDigest ledgerHash; | ||||
| @@ -55,6 +51,10 @@ public class TxBuilder implements TransactionBuilder { | |||||
| return txContent; | return txContent; | ||||
| } | } | ||||
| public Collection<OperationReturnValueHandler> getReturnValuehandlers() { | |||||
| return opFactory.getReturnValuetHandlers(); | |||||
| } | |||||
| @Override | @Override | ||||
| public LedgerInitOperationBuilder ledgers() { | public LedgerInitOperationBuilder ledgers() { | ||||
| return opFactory.ledgers(); | return opFactory.ledgers(); | ||||
| @@ -94,11 +94,6 @@ public class TxBuilder implements TransactionBuilder { | |||||
| return opFactory.contract(address, contractIntf); | return opFactory.contract(address, contractIntf); | ||||
| } | } | ||||
| @Override | |||||
| public <T> EventResult<T> result(ContractEventExecutor execute) { | |||||
| return opFactory.result(execute); | |||||
| } | |||||
| @Override | @Override | ||||
| public <T> T contract(String address, Class<T> contractIntf) { | public <T> T contract(String address, Class<T> contractIntf) { | ||||
| return opFactory.contract(address, contractIntf); | return opFactory.contract(address, contractIntf); | ||||
| @@ -71,8 +71,7 @@ public class TxRequestBuilder implements TransactionRequestBuilder { | |||||
| } | } | ||||
| public static SignatureDigest sign(TransactionContent txContent, PrivKey privKey) { | 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) { | public static boolean verifySignature(TransactionContent txContent, SignatureDigest signDigest, PubKey pubKey) { | ||||
| @@ -1,27 +1,21 @@ | |||||
| package com.jd.blockchain.transaction; | package com.jd.blockchain.transaction; | ||||
| import com.jd.blockchain.contract.EventResult; | |||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.ledger.PreparedTransaction; | import com.jd.blockchain.ledger.PreparedTransaction; | ||||
| import com.jd.blockchain.ledger.TransactionRequestBuilder; | import com.jd.blockchain.ledger.TransactionRequestBuilder; | ||||
| import com.jd.blockchain.ledger.TransactionTemplate; | import com.jd.blockchain.ledger.TransactionTemplate; | ||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| public class TxTemplate implements TransactionTemplate { | public class TxTemplate implements TransactionTemplate { | ||||
| private TxBuilder txBuilder; | private TxBuilder txBuilder; | ||||
| private TransactionService txService; | private TransactionService txService; | ||||
| private Map<Integer, EventResult> eventResults; | |||||
| public TxTemplate(HashDigest ledgerHash, TransactionService txService) { | public TxTemplate(HashDigest ledgerHash, TransactionService txService) { | ||||
| this.txBuilder = new TxBuilder(ledgerHash); | this.txBuilder = new TxBuilder(ledgerHash); | ||||
| this.txService = txService; | this.txService = txService; | ||||
| this.eventResults = new HashMap<>(); | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -32,7 +26,7 @@ public class TxTemplate implements TransactionTemplate { | |||||
| @Override | @Override | ||||
| public PreparedTransaction prepare() { | public PreparedTransaction prepare() { | ||||
| TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | ||||
| return new PreparedTx(txReqBuilder, txService, eventResults); | |||||
| return new PreparedTx(txReqBuilder, txService, txBuilder.getReturnValuehandlers()); | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -60,25 +54,11 @@ public class TxTemplate implements TransactionTemplate { | |||||
| return txBuilder.contracts(); | return txBuilder.contracts(); | ||||
| } | } | ||||
| // @Override | |||||
| // public ContractEventSendOperationBuilder contractEvents() { | |||||
| // return txBuilder.contractEvents(); | |||||
| // } | |||||
| @Override | @Override | ||||
| public <T> T contract(Bytes address, Class<T> contractIntf) { | public <T> T contract(Bytes address, Class<T> contractIntf) { | ||||
| return txBuilder.contract(address, 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 | @Override | ||||
| public <T> T contract(String address, Class<T> contractIntf) { | public <T> T contract(String address, Class<T> contractIntf) { | ||||
| return txBuilder.contract(address, contractIntf); | return txBuilder.contract(address, contractIntf); | ||||
| @@ -1,164 +1,159 @@ | |||||
| package com.jd.blockchain.sdk.samples; | 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.EventResult; | ||||
| import com.jd.blockchain.contract.TransferContract; | 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.ContractEventExecutor; | ||||
| import com.jd.blockchain.transaction.ContractReturns.ReturnLongValue; | |||||
| import com.jd.blockchain.transaction.ContractReturns.ReturnValue; | |||||
| import com.jd.blockchain.utils.Bytes; | 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 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; | 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.contract.TransferContract; | ||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.crypto.PrivKey; | import com.jd.blockchain.crypto.PrivKey; | ||||
| import com.jd.blockchain.crypto.PubKey; | 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.BlockchainService; | ||||
| import com.jd.blockchain.sdk.client.GatewayServiceFactory; | import com.jd.blockchain.sdk.client.GatewayServiceFactory; | ||||
| import com.jd.blockchain.sdk.samples.SDKDemo_Constant; | import com.jd.blockchain.sdk.samples.SDKDemo_Constant; | ||||
| import com.jd.blockchain.tools.keygen.KeyGenCommand; | 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 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 { | 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.CountDownLatch; | ||||
| import java.util.concurrent.atomic.AtomicLong; | 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.apache.commons.io.FileUtils; | ||||
| import org.springframework.core.io.ClassPathResource; | import org.springframework.core.io.ClassPathResource; | ||||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | 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.AddressEncoding; | ||||
| import com.jd.blockchain.crypto.AsymmetricKeypair; | import com.jd.blockchain.crypto.AsymmetricKeypair; | ||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.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.LedgerRepository; | ||||
| import com.jd.blockchain.ledger.core.impl.LedgerManager; | import com.jd.blockchain.ledger.core.impl.LedgerManager; | ||||
| import com.jd.blockchain.sdk.BlockchainService; | import com.jd.blockchain.sdk.BlockchainService; | ||||
| import com.jd.blockchain.storage.service.DbConnection; | import com.jd.blockchain.storage.service.DbConnection; | ||||
| import com.jd.blockchain.storage.service.DbConnectionFactory; | import com.jd.blockchain.storage.service.DbConnectionFactory; | ||||
| import com.jd.blockchain.tools.initializer.LedgerBindingConfig; | 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.Bytes; | ||||
| import com.jd.blockchain.utils.concurrent.ThreadInvoker; | import com.jd.blockchain.utils.concurrent.ThreadInvoker; | ||||
| import com.jd.blockchain.utils.net.NetworkAddress; | import com.jd.blockchain.utils.net.NetworkAddress; | ||||
| @@ -576,10 +587,7 @@ public class IntegrationBase { | |||||
| ReadContract readContract1 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | 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); | ReadContract readContract2 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | ||||
| @@ -587,10 +595,7 @@ public class IntegrationBase { | |||||
| ReadContract readContract3 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | 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(); | PreparedTransaction contractPtx = txContract.prepare(); | ||||
| @@ -602,8 +607,8 @@ public class IntegrationBase { | |||||
| OperationResult[] operationResults = readTxResp.getOperationResults(); | OperationResult[] operationResults = readTxResp.getOperationResults(); | ||||
| // 通过EventResult获取结果 | // 通过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.contract.LedgerContext; | ||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| import com.jd.blockchain.ledger.BlockchainIdentity; | import com.jd.blockchain.ledger.BlockchainIdentity; | ||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.ledger.ContractEventSendOperation; | import com.jd.blockchain.ledger.ContractEventSendOperation; | ||||
| import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
| import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
| @@ -33,7 +34,7 @@ public class MockerContractExeHandle implements OperationHandle { | |||||
| private HashDigest ledgerHash; | private HashDigest ledgerHash; | ||||
| @Override | @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) { | LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { | ||||
| ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | ||||
| @@ -76,11 +77,6 @@ public class MockerContractExeHandle implements OperationHandle { | |||||
| return ContractSerializeUtils.serialize(result); | return ContractSerializeUtils.serialize(result); | ||||
| } | } | ||||
| // @Override | |||||
| // public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||||
| // return null; | |||||
| // } | |||||
| @Override | @Override | ||||
| public boolean support(Class<?> operationType) { | public boolean support(Class<?> operationType) { | ||||
| return ContractEventSendOperation.class.isAssignableFrom(operationType); | return ContractEventSendOperation.class.isAssignableFrom(operationType); | ||||