diff --git a/source/contract/contract-samples/pom.xml b/source/contract/contract-samples/pom.xml index b229b7bc..5842f8c7 100644 --- a/source/contract/contract-samples/pom.xml +++ b/source/contract/contract-samples/pom.xml @@ -34,11 +34,11 @@ maven-assembly-plugin - contract + transfer false - com.jd.blockchain.contract.ReadContractImpl + com.jd.blockchain.contract.TransferContractImpl diff --git a/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/TransferContract.java b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/TransferContract.java new file mode 100644 index 00000000..321101a3 --- /dev/null +++ b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/TransferContract.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.contract; + +@Contract +public interface TransferContract { + + @ContractEvent(name = "create") + String create(String address, String account, long money); + + @ContractEvent(name = "transfer") + String transfer(String address, String from, String to, long money); + + @ContractEvent(name = "read") + long read(String address, String account); + + @ContractEvent(name = "readAll") + String readAll(String address, String account); +} diff --git a/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/TransferContractImpl.java b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/TransferContractImpl.java new file mode 100644 index 00000000..1a3d702b --- /dev/null +++ b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/TransferContractImpl.java @@ -0,0 +1,109 @@ +package com.jd.blockchain.contract; + +import com.alibaba.fastjson.JSON; +import com.jd.blockchain.crypto.HashDigest; +import com.jd.blockchain.ledger.KVDataEntry; +import com.jd.blockchain.ledger.KVDataVO; +import com.jd.blockchain.ledger.KVInfoVO; + +public class TransferContractImpl implements EventProcessingAwire, TransferContract { + + private ContractEventContext eventContext; + + private HashDigest ledgerHash; + + @Override + public String create(String address, String account, long money) { + KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); + // 肯定有返回值,但若不存在则返回version=-1 + if (kvDataEntries != null && kvDataEntries.length > 0) { + long currVersion = kvDataEntries[0].getVersion(); + if (currVersion > -1) { + throw new IllegalStateException(String.format("%s -> %s already have created !!!", address, account)); + } + eventContext.getLedger().dataAccount(address).setInt64(account, money, -1L); + } else { + throw new IllegalStateException(String.format("Ledger[%s] inner Error !!!", ledgerHash.toBase58())); + } + return String.format("DataAccountAddress[%s] -> Create(By Contract Operation) Account = %s and Money = %s Success!!! \r\n", + address, account, money); + } + + @Override + public String transfer(String address, String from, String to, long money) { + // 首先查询余额 + KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, from, to); + if (kvDataEntries == null || kvDataEntries.length != 2) { + throw new IllegalStateException(String.format("%s -> %s - %s may be not created !!!", address, from, to)); + } else { + // 判断from账号中钱数量是否足够 + long fromMoney = 0L, toMoney = 0L, fromVersion = 0L, toVersion = 0L; + for (KVDataEntry kvDataEntry : kvDataEntries) { + if (kvDataEntry.getKey().equals(from)) { + fromMoney = (long) kvDataEntry.getValue(); + fromVersion = kvDataEntry.getVersion(); + } else { + toMoney = (long) kvDataEntry.getValue(); + toVersion = kvDataEntry.getVersion(); + } + } + if (fromMoney < money) { + throw new IllegalStateException(String.format("%s -> %s not have enough money !!!", address, from)); + } + long fromNewMoney = fromMoney - money; + long toNewMoney = toMoney + money; + // 重新设置 + eventContext.getLedger().dataAccount(address).setInt64(from, fromNewMoney, fromVersion); + eventContext.getLedger().dataAccount(address).setInt64(to, toNewMoney, toVersion); + } + + return String.format("DataAccountAddress[%s] transfer from [%s] to [%s] and [money = %s] Success !!!", address, from, to, money); + } + + @Override + public long read(String address, String account) { + KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); + if (kvDataEntries == null || kvDataEntries.length == 0) { + return -1; + } + return (long)kvDataEntries[0].getValue(); + } + + @Override + public String readAll(String address, String account) { + KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); + // 获取最新的版本号 + if (kvDataEntries == null || kvDataEntries.length == 0) { + return ""; + } + long newestVersion = kvDataEntries[0].getVersion(); + if (newestVersion == -1) { + return ""; + } + KVDataVO[] kvDataVOS = new KVDataVO[1]; + long[] versions = new long[(int)newestVersion + 1]; + for (int i = 0; i < versions.length; i++) { + versions[i] = i; + } + KVDataVO kvDataVO = new KVDataVO(account, versions); + + kvDataVOS[0] = kvDataVO; + + KVInfoVO kvInfoVO = new KVInfoVO(kvDataVOS); + + KVDataEntry[] allEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, kvInfoVO); + + return JSON.toJSONString(allEntries); + } + + @Override + public void beforeEvent(ContractEventContext eventContext) { + this.eventContext = eventContext; + this.ledgerHash = eventContext.getCurrentLedgerHash(); + } + + @Override + public void postEvent(ContractEventContext eventContext, Exception error) { + + } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataAccountKVSetOperation.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataAccountKVSetOperation.java index 5bedb8bc..c83cea9a 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataAccountKVSetOperation.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataAccountKVSetOperation.java @@ -7,7 +7,7 @@ import com.jd.blockchain.consts.DataCodes; import com.jd.blockchain.utils.Bytes; @DataContract(code= DataCodes.TX_OP_DATA_ACC_SET) -public interface DataAccountKVSetOperation extends Operation{ +public interface DataAccountKVSetOperation extends Operation { @DataField(order=2, primitiveType=PrimitiveType.BYTES) Bytes getAccountAddress(); diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataType.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataType.java index a5361695..ac6e4b44 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataType.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataType.java @@ -106,9 +106,6 @@ public enum DataType { ENCRYPTED_DATA((byte) (BaseType.BYTES | 0x08)); - - - @EnumField(type = PrimitiveType.INT8) public final byte CODE; diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVDataVO.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVDataVO.java index 4fc214d0..10ffad7a 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVDataVO.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVDataVO.java @@ -8,6 +8,14 @@ public class KVDataVO { private String key; private long[] version; + public KVDataVO() { + } + + public KVDataVO(String key, long[] version) { + this.key = key; + this.version = version; + } + public String getKey() { return key; } diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVInfoVO.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVInfoVO.java index 5f49e3de..2077810d 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVInfoVO.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVInfoVO.java @@ -8,6 +8,13 @@ package com.jd.blockchain.ledger; public class KVInfoVO { private KVDataVO[] data; + public KVInfoVO() { + } + + public KVInfoVO(KVDataVO[] data) { + this.data = data; + } + public KVDataVO[] getData() { return data; } diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxy.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxy.java index 617a74c7..465bb9f5 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxy.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxy.java @@ -61,7 +61,7 @@ public class ContractInvocationProxy implements InvocationHandler { ((ContractEventSendOpTemplate) sendOperation).setEventAndArgs(event, argBytes); } // 代理操作,返回值类型无法创建 - return null; + return returnResult(method.getReturnType()); } private byte[] serializeArgs(Object[] args) { @@ -74,4 +74,36 @@ public class ContractInvocationProxy implements InvocationHandler { } 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; + } + } } diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractType.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractType.java index a2602a4b..34acfb5e 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractType.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractType.java @@ -60,6 +60,23 @@ public class ContractType { public static ContractType resolve(Class contractIntf) { + // 如果是Class则首先获取其接口 + if (!contractIntf.isInterface()) { + Class realIntf = null; + Class[] interfaces = contractIntf.getInterfaces(); + for (Class intf: interfaces) { + if (intf.isAnnotationPresent(Contract.class)) { + realIntf = intf; + break; + } + } + if (realIntf == null) { + throw new IllegalDataException(String.format( + "%s is not a Contract Type, because there is not @Contract !", contractIntf.getName())); + } + contractIntf = realIntf; + } + // 接口上必须有注解 if (!contractIntf.isAnnotationPresent(Contract.class)) { throw new IllegalDataException("It is not a Contract Type, because there is not @Contract !"); diff --git a/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/ClientOperationUtil.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/ClientResolveUtil.java similarity index 78% rename from source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/ClientOperationUtil.java rename to source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/ClientResolveUtil.java index 02a8f12f..1eac59c7 100644 --- a/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/ClientOperationUtil.java +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/ClientResolveUtil.java @@ -1,46 +1,26 @@ /** * Copyright: Copyright 2016-2020 JD.COM All Right Reserved - * FileName: com.jd.blockchain.sdk.client.ClientOperationUtil + * FileName: com.jd.blockchain.sdk.client.ClientResolveUtil * Author: shaozhuguang * Department: Y事业部 * Date: 2019/3/27 下午4:12 * Description: */ -package com.jd.blockchain.sdk.client; - -import java.lang.reflect.Field; - -import org.apache.commons.codec.binary.Base64; +package com.jd.blockchain.sdk.converters; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.jd.blockchain.crypto.CryptoProvider; import com.jd.blockchain.crypto.PubKey; -import com.jd.blockchain.ledger.BlockchainIdentityData; -import com.jd.blockchain.ledger.BytesValue; -import com.jd.blockchain.ledger.BytesValueEntry; -import com.jd.blockchain.ledger.DataType; -import com.jd.blockchain.ledger.ContractCodeDeployOperation; -import com.jd.blockchain.ledger.ContractEventSendOperation; -import com.jd.blockchain.ledger.CryptoSetting; -import com.jd.blockchain.ledger.DataAccountKVSetOperation; -import com.jd.blockchain.ledger.DataAccountRegisterOperation; -import com.jd.blockchain.ledger.LedgerInitOperation; -import com.jd.blockchain.ledger.Operation; -import com.jd.blockchain.ledger.ParticipantNode; -import com.jd.blockchain.ledger.UserRegisterOperation; -import com.jd.blockchain.transaction.ContractCodeDeployOpTemplate; -import com.jd.blockchain.transaction.ContractEventSendOpTemplate; -import com.jd.blockchain.transaction.DataAccountKVSetOpTemplate; -import com.jd.blockchain.transaction.DataAccountRegisterOpTemplate; -import com.jd.blockchain.transaction.KVData; -import com.jd.blockchain.transaction.LedgerInitOpTemplate; -import com.jd.blockchain.transaction.LedgerInitSettingData; -import com.jd.blockchain.transaction.UserRegisterOpTemplate; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.transaction.*; import com.jd.blockchain.utils.Bytes; import com.jd.blockchain.utils.codec.Base58Utils; import com.jd.blockchain.utils.codec.HexUtils; import com.jd.blockchain.utils.io.BytesUtils; +import org.apache.commons.codec.binary.Base64; + +import java.lang.reflect.Field; /** * @@ -49,7 +29,42 @@ import com.jd.blockchain.utils.io.BytesUtils; * @since 1.0.0 */ -public class ClientOperationUtil { +public class ClientResolveUtil { + + public static KVDataEntry[] read(KVDataEntry[] kvDataEntries) { + if (kvDataEntries == null || kvDataEntries.length == 0) { + return kvDataEntries; + } + KVDataEntry[] resolveKvDataEntries = new KVDataEntry[kvDataEntries.length]; + // kvDataEntries是代理对象,需要处理 + for (int i = 0; i < kvDataEntries.length; i++) { + KVDataEntry kvDataEntry = kvDataEntries[i]; + String key = kvDataEntry.getKey(); + long version = kvDataEntry.getVersion(); + DataType dataType = kvDataEntry.getType(); + KvData innerKvData = new KvData(key, version, dataType); + Object valueObj = kvDataEntry.getValue(); + switch (dataType) { + case NIL: + break; + case BYTES: + case TEXT: + case JSON: + innerKvData.setValue(valueObj.toString()); + break; + case INT32: + innerKvData.setValue(Integer.parseInt(valueObj.toString())); + break; + case INT64: + innerKvData.setValue(Long.parseLong(valueObj.toString())); + break; + default: + throw new IllegalStateException("Unsupported value type[" + dataType + "] to resolve!"); + } + resolveKvDataEntries[i] = innerKvData; + } + return resolveKvDataEntries; + } public static Operation read(Operation operation) { @@ -297,4 +312,65 @@ public class ClientOperationUtil { this.id = id; } } + + public static class KvData implements KVDataEntry { + + private String key; + + private long version; + + private DataType dataType; + + private Object value; + + public KvData() { + } + + public KvData(String key, long version, DataType dataType) { + this(key, version, dataType, null); + } + + public KvData(String key, long version, DataType dataType, Object value) { + this.key = key; + this.version = version; + this.dataType = dataType; + this.value = value; + } + + public void setKey(String key) { + this.key = key; + } + + public void setVersion(long version) { + this.version = version; + } + + public void setDataType(DataType dataType) { + this.dataType = dataType; + } + + public void setValue(Object value) { + this.value = value; + } + + @Override + public String getKey() { + return key; + } + + @Override + public long getVersion() { + return version; + } + + @Override + public DataType getType() { + return dataType; + } + + @Override + public Object getValue() { + return value; + } + } } \ No newline at end of file diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/BlockchainServiceProxy.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/BlockchainServiceProxy.java index e18f1df7..4875def3 100644 --- a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/BlockchainServiceProxy.java +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/BlockchainServiceProxy.java @@ -5,6 +5,7 @@ import com.jd.blockchain.ledger.*; import com.jd.blockchain.sdk.BlockchainEventHandle; import com.jd.blockchain.sdk.BlockchainEventListener; import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.converters.ClientResolveUtil; import com.jd.blockchain.transaction.BlockchainQueryService; import com.jd.blockchain.transaction.TransactionService; import com.jd.blockchain.transaction.TxTemplate; @@ -144,17 +145,20 @@ public abstract class BlockchainServiceProxy implements BlockchainService { @Override public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { - return getQueryService(ledgerHash).getDataEntries(ledgerHash, address, keys); + KVDataEntry[] kvDataEntries = getQueryService(ledgerHash).getDataEntries(ledgerHash, address, keys); + return ClientResolveUtil.read(kvDataEntries); } @Override public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO) { - return getQueryService(ledgerHash).getDataEntries(ledgerHash, address, kvInfoVO); + KVDataEntry[] kvDataEntries = getQueryService(ledgerHash).getDataEntries(ledgerHash, address, kvInfoVO); + return ClientResolveUtil.read(kvDataEntries); } @Override public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { - return getQueryService(ledgerHash).getDataEntries(ledgerHash, address, fromIndex, count); + KVDataEntry[] kvDataEntries = getQueryService(ledgerHash).getDataEntries(ledgerHash, address, fromIndex, count); + return ClientResolveUtil.read(kvDataEntries); } @Override diff --git a/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/ClientResolveUtil.java b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/ClientResolveUtil.java new file mode 100644 index 00000000..72cd4cb8 --- /dev/null +++ b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/ClientResolveUtil.java @@ -0,0 +1,385 @@ +///** +// * Copyright: Copyright 2016-2020 JD.COM All Right Reserved +// * FileName: com.jd.blockchain.sdk.client.ClientResolveUtil +// * Author: shaozhuguang +// * Department: Y事业部 +// * Date: 2019/3/27 下午4:12 +// * Description: +// */ +//package com.jd.blockchain.sdk.client; +// +//import java.lang.reflect.Field; +// +//import com.jd.blockchain.ledger.*; +//import com.jd.blockchain.utils.io.ByteArray; +//import org.apache.commons.codec.binary.Base64; +// +//import com.alibaba.fastjson.JSONArray; +//import com.alibaba.fastjson.JSONObject; +//import com.jd.blockchain.crypto.CryptoProvider; +//import com.jd.blockchain.crypto.PubKey; +//import com.jd.blockchain.transaction.ContractCodeDeployOpTemplate; +//import com.jd.blockchain.transaction.ContractEventSendOpTemplate; +//import com.jd.blockchain.transaction.DataAccountKVSetOpTemplate; +//import com.jd.blockchain.transaction.DataAccountRegisterOpTemplate; +//import com.jd.blockchain.transaction.KVData; +//import com.jd.blockchain.transaction.LedgerInitOpTemplate; +//import com.jd.blockchain.transaction.LedgerInitSettingData; +//import com.jd.blockchain.transaction.UserRegisterOpTemplate; +//import com.jd.blockchain.utils.Bytes; +//import com.jd.blockchain.utils.codec.Base58Utils; +//import com.jd.blockchain.utils.codec.HexUtils; +//import com.jd.blockchain.utils.io.BytesUtils; +// +///** +// * +// * @author shaozhuguang +// * @create 2019/3/27 +// * @since 1.0.0 +// */ +// +//public class ClientResolveUtil { +// +// public static KVDataEntry[] read(KVDataEntry[] kvDataEntries) { +// if (kvDataEntries == null || kvDataEntries.length == 0) { +// return kvDataEntries; +// } +// KVDataEntry[] resolveKvDataEntries = new KVDataEntry[kvDataEntries.length]; +// // kvDataEntries是代理对象,需要处理 +// for (int i = 0; i < kvDataEntries.length; i++) { +// KVDataEntry kvDataEntry = kvDataEntries[i]; +// String key = kvDataEntry.getKey(); +// long version = kvDataEntry.getVersion(); +// DataType dataType = kvDataEntry.getType(); +// KvData innerKvData = new KvData(key, version, dataType); +// Object valueObj = kvDataEntry.getValue(); +// switch (dataType) { +// case NIL: +// break; +// case BYTES: +// case TEXT: +// case JSON: +// innerKvData.setValue(valueObj.toString()); +// break; +// case INT32: +// innerKvData.setValue(Integer.parseInt(valueObj.toString())); +// break; +// case INT64: +// innerKvData.setValue(Long.parseLong(valueObj.toString())); +// break; +// default: +// throw new IllegalStateException("Unsupported value type[" + dataType + "] to resolve!"); +// } +// resolveKvDataEntries[i] = innerKvData; +// } +// return resolveKvDataEntries; +// } +// +// public static Operation read(Operation operation) { +// +// try { +// // Class +// Class clazz = operation.getClass(); +// Field field = clazz.getSuperclass().getDeclaredField("h"); +// field.setAccessible(true); +// Object object = field.get(operation); +// if (object instanceof JSONObject) { +// JSONObject jsonObject = (JSONObject) object; +// if (jsonObject.containsKey("accountID")) { +// return convertDataAccountRegisterOperation(jsonObject); +// } else if (jsonObject.containsKey("userID")) { +// return convertUserRegisterOperation(jsonObject); +// } else if (jsonObject.containsKey("contractID")) { +// return convertContractCodeDeployOperation(jsonObject); +// } else if (jsonObject.containsKey("writeSet")) { +// return convertDataAccountKVSetOperation(jsonObject); +// } else if (jsonObject.containsKey("initSetting")) { +// return convertLedgerInitOperation(jsonObject); +// } else if (jsonObject.containsKey("contractAddress")) { +// return convertContractEventSendOperation(jsonObject); +// } +// } +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// +// return null; +// } +// +// public static Object readValueByBytesValue(BytesValue bytesValue) { +// DataType dataType = bytesValue.getType(); +// Bytes saveVal = bytesValue.getValue(); +// Object showVal; +// switch (dataType) { +// case BYTES: +// // return hex +// showVal = HexUtils.encode(saveVal.toBytes()); +// break; +// case TEXT: +// case JSON: +// showVal = saveVal.toUTF8String(); +// break; +// case INT64: +// showVal = BytesUtils.toLong(saveVal.toBytes()); +// break; +// default: +// showVal = HexUtils.encode(saveVal.toBytes()); +// break; +// } +// return showVal; +// } +// +// public static DataAccountRegisterOperation convertDataAccountRegisterOperation(JSONObject jsonObject) { +// JSONObject account = jsonObject.getJSONObject("accountID"); +// return new DataAccountRegisterOpTemplate(blockchainIdentity(account)); +// } +// +// public static DataAccountKVSetOperation convertDataAccountKVSetOperation(JSONObject jsonObject) { +// // 写入集合处理 +// JSONArray writeSetObj = jsonObject.getJSONArray("writeSet"); +// JSONObject accountAddrObj = jsonObject.getJSONObject("accountAddress"); +// String addressBase58 = accountAddrObj.getString("value"); +// Bytes address = Bytes.fromBase58(addressBase58); +// +// DataAccountKVSetOpTemplate kvOperation = new DataAccountKVSetOpTemplate(address); +// for (int i = 0; i tools-initializer ${project.version} + + com.jd.blockchain + contract-samples + ${project.version} + diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Constant.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Constant.java new file mode 100644 index 00000000..aa5ac2f7 --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Constant.java @@ -0,0 +1,38 @@ +package com.jd.blockchain.sdk.samples; + +import org.apache.commons.io.FileUtils; +import org.springframework.core.io.ClassPathResource; + +import java.io.File; + +public class SDKDemo_Constant { + + public static final String GW_IPADDR = "127.0.0.1"; + + public static final int GW_PORT = 11000; + + public static final String[] PUB_KEYS = { + "3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9", + "3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX", + "3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x", + "3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk"}; + + public static final String[] PRIV_KEYS = { + "177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x", + "177gju9p5zrNdHJVEQnEEKF4ZjDDYmAXyfG84V5RPGVc5xFfmtwnHA7j51nyNLUFffzz5UT", + "177gjtwLgmSx5v1hFb46ijh7L9kdbKUpJYqdKVf9afiEmAuLgo8Rck9yu5UuUcHknWJuWaF", + "177gk1pudweTq5zgJTh8y3ENCTwtSFsKyX7YnpuKPo7rKgCkCBXVXh5z2syaTCPEMbuWRns"}; + + public static final String PASSWORD = "abc"; + + public static final byte[] readChainCodes(String contractZip) { + // 构建合约的字节数组; + try { + ClassPathResource contractPath = new ClassPathResource(contractZip); + File contractFile = new File(contractPath.getURI()); + return FileUtils.readFileToByteArray(contractFile); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } +} diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDK_Base_Demo.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDK_Base_Demo.java new file mode 100644 index 00000000..bbd4ed71 --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDK_Base_Demo.java @@ -0,0 +1,51 @@ +package com.jd.blockchain.sdk.samples; + +import com.jd.blockchain.crypto.HashDigest; +import com.jd.blockchain.crypto.PrivKey; +import com.jd.blockchain.crypto.PubKey; +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.tools.keygen.KeyGenCommand; + +public abstract class SDK_Base_Demo { + + protected BlockchainKeypair adminKey; + + protected HashDigest ledgerHash; + + protected BlockchainService blockchainService; + + public SDK_Base_Demo() { + init(); + } + + public void init() { + // 生成连接网关的账号 + PrivKey privKey = KeyGenCommand.decodePrivKeyWithRawPassword(SDKDemo_Constant.PRIV_KEYS[0], SDKDemo_Constant.PASSWORD); + + PubKey pubKey = KeyGenCommand.decodePubKey(SDKDemo_Constant.PUB_KEYS[0]); + + adminKey = new BlockchainKeypair(pubKey, privKey); + + // 连接网关 + GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(SDKDemo_Constant.GW_IPADDR, + SDKDemo_Constant.GW_PORT, false, adminKey); + + // 获取网关对应的Service处理类 + blockchainService = serviceFactory.getBlockchainService(); + + HashDigest[] ledgerHashs = blockchainService.getLedgerHashs(); + // 获取当前账本Hash + ledgerHash = ledgerHashs[0]; + } + + public TransactionResponse commit(TransactionTemplate txTpl) { + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + return ptx.commit(); + } +} diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDK_Contract_Demo.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDK_Contract_Demo.java new file mode 100644 index 00000000..15a023f4 --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDK_Contract_Demo.java @@ -0,0 +1,164 @@ +package com.jd.blockchain.sdk.samples; + +import com.jd.blockchain.contract.EventResult; +import com.jd.blockchain.contract.TransferContract; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.transaction.ContractEventExecutor; +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 eventResult = txTpl.result((ContractEventExecutor) () -> { + 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 eventResult = txTpl.result((ContractEventExecutor) () -> { + 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 eventResult = txTpl.result((ContractEventExecutor) () -> { + 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 eventResult = txTpl.result((ContractEventExecutor) () -> { + 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); + } + } +} diff --git a/source/sdk/sdk-samples/src/main/resources/transfer.jar b/source/sdk/sdk-samples/src/main/resources/transfer.jar new file mode 100644 index 00000000..a161392e Binary files /dev/null and b/source/sdk/sdk-samples/src/main/resources/transfer.jar differ diff --git a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDKDemo_Contract_Test.java b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDKDemo_Contract_Test.java new file mode 100644 index 00000000..3aa3035c --- /dev/null +++ b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDKDemo_Contract_Test.java @@ -0,0 +1,171 @@ +package test.com.jd.blockchain.sdk.test; + +import com.jd.blockchain.contract.EventResult; +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.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.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 HashDigest ledgerHash; + + private BlockchainService blockchainService; + + @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]); + + adminKey = new BlockchainKeypair(pubKey, privKey); + + // 连接网关 + GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(SDKDemo_Constant.GW_IPADDR, + SDKDemo_Constant.GW_PORT, false, adminKey); + + blockchainService = serviceFactory.getBlockchainService(); + + HashDigest[] ledgerHashs = blockchainService.getLedgerHashs(); + + ledgerHash = ledgerHashs[0]; + } + + @Test + public void testContract() { + + // 发布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 eventResult = txTpl.result((ContractEventExecutor) () -> { + 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 eventResult = txTpl.result((ContractEventExecutor) () -> { + 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 eventResult = txTpl.result((ContractEventExecutor) () -> { + 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 eventResult = txTpl.result((ContractEventExecutor) () -> { + 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(); + } +} diff --git a/source/sdk/sdk-samples/src/test/resources/transfer.jar b/source/sdk/sdk-samples/src/test/resources/transfer.jar new file mode 100644 index 00000000..a161392e Binary files /dev/null and b/source/sdk/sdk-samples/src/test/resources/transfer.jar differ diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java index 87d886ea..66aa8b5e 100644 --- a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java @@ -24,6 +24,7 @@ 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.*; @@ -556,9 +557,13 @@ public class IntegrationBase { // 再提交一个KV写入 String key1 = "JingDong", value1 = "www.jd.com"; String key2 = "JD", value2 = "JingDong"; + String key3 = "Test", value3 = "OK"; TransactionTemplate txKv = blockchainService.newTransaction(ledgerHash); - txKv.dataAccount(newDataAccount.getAddress()).setText(key1, value1, -1).setText(key2, value2, -1); + txKv.dataAccount(newDataAccount.getAddress()) + .setText(key1, value1, -1) + .setBytes(key2, Bytes.fromString(value2), -1) + .setBytes(key3, Bytes.fromString(value3).toBytes(), -1); PreparedTransaction kvPtx = txKv.prepare(); kvPtx.sign(adminKey); @@ -578,10 +583,7 @@ public class IntegrationBase { ReadContract readContract2 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); - EventResult read2 = txContract.result((ContractEventExecutor) () -> { - readContract2.read(newDataAccount.getAddress().toBase58(), key2); - return readContract2; - }); + readContract2.read(newDataAccount.getAddress().toBase58(), key2); ReadContract readContract3 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); @@ -601,14 +603,13 @@ public class IntegrationBase { // 通过EventResult获取结果 System.out.printf("readContract1.result = %s \r\n", read1.get()); - System.out.printf("readContract2.result = %s \r\n", read2.get()); System.out.printf("readContract3.result = %s \r\n", read3.get()); // 打印结果 -// for (OperationResult or : operationResults) { -// System.out.printf("操作[%s].Result = %s \r\n", or.getIndex(), or.getResult()); -// } + for (OperationResult or : operationResults) { + System.out.printf("操作[%s].Result = %s \r\n", or.getIndex(), ContractSerializeUtils.resolve(or.getResult())); + } // // // 验证结果 // assertNotNull(contractReturn);