@@ -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); | ||||