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