@@ -79,7 +79,17 @@ public interface DataCodes { | |||||
public static final int DATA = 0x900; | public static final int DATA = 0x900; | ||||
//contract related; | |||||
public static final int CONTRACT = 0xA00; | public static final int CONTRACT = 0xA00; | ||||
public static final int CONTRACT_INT8 = 0xA01; | |||||
public static final int CONTRACT_INT16 = 0xA02; | |||||
public static final int CONTRACT_INT32 = 0xA03; | |||||
public static final int CONTRACT_INT64 = 0xA04; | |||||
public static final int CONTRACT_TEXT = 0xA05; | |||||
public static final int CONTRACT_BINARY = 0xA06; | |||||
public static final int CONTRACT_BIG_INT = 0xA07; | |||||
//...0xA19 | |||||
public static final int CONTRACT_BIZ_CONTENT = 0xA20; | |||||
public static final int HASH = 0xB00; | public static final int HASH = 0xB00; | ||||
@@ -27,7 +27,7 @@ import java.lang.annotation.Target; | |||||
* @author huanghaiquan | * @author huanghaiquan | ||||
* | * | ||||
*/ | */ | ||||
@Target({ ElementType.TYPE }) | |||||
@Target({ ElementType.TYPE, ElementType.PARAMETER }) | |||||
@Retention(RetentionPolicy.RUNTIME) | @Retention(RetentionPolicy.RUNTIME) | ||||
public @interface DataContract { | public @interface DataContract { | ||||
@@ -1,24 +1,24 @@ | |||||
package com.jd.blockchain.contract.jvm; | package com.jd.blockchain.contract.jvm; | ||||
import java.lang.annotation.Annotation; | |||||
import java.lang.reflect.Method; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.util.ReflectionUtils; | |||||
import com.jd.blockchain.contract.ContractEvent; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.contract.ContractEventContext; | import com.jd.blockchain.contract.ContractEventContext; | ||||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||||
import com.jd.blockchain.contract.engine.ContractCode; | import com.jd.blockchain.contract.engine.ContractCode; | ||||
import com.jd.blockchain.runtime.Module; | import com.jd.blockchain.runtime.Module; | ||||
import com.jd.blockchain.utils.BaseConstant; | |||||
import com.jd.blockchain.transaction.ContractType; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
import com.jd.blockchain.utils.IllegalDataException; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.util.ReflectionUtils; | |||||
import java.lang.reflect.Method; | |||||
import java.util.List; | |||||
/** | /** | ||||
* contract code based jvm | * contract code based jvm | ||||
* @author zhaogw | |||||
* | |||||
* @author zhaogw | |||||
*/ | */ | ||||
public class JavaContractCode implements ContractCode { | public class JavaContractCode implements ContractCode { | ||||
private static final Logger LOGGER = LoggerFactory.getLogger(JavaContractCode.class); | private static final Logger LOGGER = LoggerFactory.getLogger(JavaContractCode.class); | ||||
@@ -26,7 +26,9 @@ public class JavaContractCode implements ContractCode { | |||||
private Bytes address; | private Bytes address; | ||||
private long version; | private long version; | ||||
private ContractEventContext contractEventContext; | private ContractEventContext contractEventContext; | ||||
private ContractType contractType; | |||||
public JavaContractCode(Bytes address, long version, Module codeModule) { | public JavaContractCode(Bytes address, long version, Module codeModule) { | ||||
this.address = address; | this.address = address; | ||||
this.version = version; | this.version = version; | ||||
@@ -37,7 +39,7 @@ public class JavaContractCode implements ContractCode { | |||||
public Bytes getAddress() { | public Bytes getAddress() { | ||||
return address; | return address; | ||||
} | } | ||||
@Override | @Override | ||||
public long getVersion() { | public long getVersion() { | ||||
return version; | return version; | ||||
@@ -46,75 +48,62 @@ public class JavaContractCode implements ContractCode { | |||||
@Override | @Override | ||||
public void processEvent(ContractEventContext eventContext) { | public void processEvent(ContractEventContext eventContext) { | ||||
this.contractEventContext = eventContext; | this.contractEventContext = eventContext; | ||||
codeModule.execute(new ContractThread()); | |||||
codeModule.execute(new ContractExecution()); | |||||
} | } | ||||
class ContractThread implements Runnable{ | |||||
public void run(){ | |||||
private Object resolveArgs(byte[] args, List<DataContract> dataContractList) { | |||||
if(args == null || args.length == 0){ | |||||
return null; | |||||
} | |||||
return ContractSerializeUtils.deserializeMethodParam(args,dataContractList); | |||||
} | |||||
class ContractExecution implements Runnable { | |||||
public void run() { | |||||
LOGGER.info("ContractThread execute()."); | LOGGER.info("ContractThread execute()."); | ||||
try { | try { | ||||
//执行预处理; | |||||
// 执行预处理; | |||||
long startTime = System.currentTimeMillis(); | long startTime = System.currentTimeMillis(); | ||||
String contractClassName = codeModule.getMainClass(); | String contractClassName = codeModule.getMainClass(); | ||||
Class myClass = codeModule.loadClass(contractClassName); | Class myClass = codeModule.loadClass(contractClassName); | ||||
Object contractMainClassObj = myClass.newInstance();//合约主类生成的类实例; | |||||
Object contractMainClassObj = myClass.newInstance();// 合约主类生成的类实例; | |||||
Method beforeMth_ = myClass.getMethod("beforeEvent",codeModule.loadClass(ContractEventContext.class.getName())); | |||||
ReflectionUtils.invokeMethod(beforeMth_,contractMainClassObj,contractEventContext); | |||||
LOGGER.info("beforeEvent,耗时:"+(System.currentTimeMillis()-startTime)); | |||||
Method beforeMth_ = myClass.getMethod("beforeEvent", | |||||
codeModule.loadClass(ContractEventContext.class.getName())); | |||||
ReflectionUtils.invokeMethod(beforeMth_, contractMainClassObj, contractEventContext); | |||||
LOGGER.info("beforeEvent,耗时:" + (System.currentTimeMillis() - startTime)); | |||||
Method eventMethod = this.getMethodByAnno(contractMainClassObj,contractEventContext.getEvent()); | |||||
// Method eventMethod = this.getMethodByAnno(contractMainClassObj, contractEventContext.getEvent()); | |||||
startTime = System.currentTimeMillis(); | startTime = System.currentTimeMillis(); | ||||
ReflectionUtils.invokeMethod(eventMethod,contractMainClassObj,contractEventContext); | |||||
// 反序列化参数; | |||||
contractType = ContractType.resolve(myClass); | |||||
Method handleMethod = contractType.getHandleMethod(contractEventContext.getEvent()); | |||||
if (handleMethod == null){ | |||||
throw new IllegalDataException("don't get this method by it's @ContractEvent."); | |||||
} | |||||
Object args = resolveArgs(contractEventContext.getArgs(), | |||||
contractType.getDataContractMap().get(handleMethod)); | |||||
LOGGER.info("合约执行,耗时:"+(System.currentTimeMillis()-startTime)); | |||||
Object[] params = null; | |||||
if(args.getClass().isArray()){ | |||||
params = (Object[])args; | |||||
} | |||||
ReflectionUtils.invokeMethod(handleMethod, contractMainClassObj, params); | |||||
LOGGER.info("合约执行,耗时:" + (System.currentTimeMillis() - startTime)); | |||||
Method mth2 = myClass.getMethod("postEvent"); | Method mth2 = myClass.getMethod("postEvent"); | ||||
startTime = System.currentTimeMillis(); | startTime = System.currentTimeMillis(); | ||||
ReflectionUtils.invokeMethod(mth2,contractMainClassObj); | |||||
LOGGER.info("postEvent,耗时:"+(System.currentTimeMillis()-startTime)); | |||||
ReflectionUtils.invokeMethod(mth2, contractMainClassObj); | |||||
LOGGER.info("postEvent,耗时:" + (System.currentTimeMillis() - startTime)); | |||||
} catch (NoSuchMethodException e) { | } catch (NoSuchMethodException e) { | ||||
e.printStackTrace(); | |||||
throw new IllegalArgumentException(e.getMessage()); | |||||
} catch (Exception e) { | } catch (Exception e) { | ||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
//得到当前类中相关方法和注解对应关系; | |||||
Method getMethodByAnno(Object classObj, String eventName){ | |||||
Class<?> c = classObj.getClass(); | |||||
Class <ContractEvent> contractEventClass = null; | |||||
try { | |||||
contractEventClass = (Class <ContractEvent>)c.getClassLoader().loadClass(ContractEvent.class.getName()); | |||||
} catch (ClassNotFoundException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
Method[] classMethods = c.getMethods(); | |||||
Map<Method, Annotation[]> methodAnnoMap = new HashMap<Method, Annotation[]>(); | |||||
Map<String,Method> annoMethodMap = new HashMap<String,Method>(); | |||||
for(int i = 0;i<classMethods.length;i++){ | |||||
Annotation[] a = classMethods[i].getDeclaredAnnotations(); | |||||
methodAnnoMap.put(classMethods[i], a); | |||||
//如果当前方法中包含@ContractEvent注解,则将其放入Map; | |||||
for(Annotation annotation_ : a){ | |||||
//如果是合同事件类型,则放入map; | |||||
if(classMethods[i].isAnnotationPresent(contractEventClass)){ | |||||
Object obj = classMethods[i].getAnnotation(contractEventClass); | |||||
String annoAllName = obj.toString(); | |||||
//format:@com.jd.blockchain.contract.model.ContractEvent(name=transfer-asset) | |||||
String eventName_ = obj.toString().substring(BaseConstant.CONTRACT_EVENT_PREFIX.length(),annoAllName.length()-1); | |||||
annoMethodMap.put(eventName_,classMethods[i]); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
if(annoMethodMap.containsKey(eventName)){ | |||||
return annoMethodMap.get(eventName); | |||||
}else { | |||||
return null; | |||||
throw new IllegalDataException(e.getMessage()); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,5 +1,6 @@ | |||||
package com.jd.blockchain.crypto.utils.sm; | package com.jd.blockchain.crypto.utils.sm; | ||||
import com.jd.blockchain.utils.io.BytesUtils; | |||||
import org.bouncycastle.asn1.ASN1Encodable; | import org.bouncycastle.asn1.ASN1Encodable; | ||||
import org.bouncycastle.asn1.ASN1Integer; | import org.bouncycastle.asn1.ASN1Integer; | ||||
import org.bouncycastle.asn1.ASN1Sequence; | import org.bouncycastle.asn1.ASN1Sequence; | ||||
@@ -98,7 +99,7 @@ public class SM2Utils { | |||||
public static byte[] sign(byte[] data, byte[] privateKey, SecureRandom random, String ID){ | public static byte[] sign(byte[] data, byte[] privateKey, SecureRandom random, String ID){ | ||||
ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1,privateKey), DOMAIN_PARAMS); | ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1,privateKey), DOMAIN_PARAMS); | ||||
CipherParameters params = new ParametersWithID(new ParametersWithRandom(privKey,random),ID.getBytes()); | |||||
CipherParameters params = new ParametersWithID(new ParametersWithRandom(privKey,random), BytesUtils.toBytes(ID)); | |||||
return sign(data,params); | return sign(data,params); | ||||
} | } | ||||
@@ -152,8 +153,7 @@ public class SM2Utils { | |||||
ECPoint pubKeyPoint = resolvePubKeyBytes(publicKey); | ECPoint pubKeyPoint = resolvePubKeyBytes(publicKey); | ||||
ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubKeyPoint, DOMAIN_PARAMS); | ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubKeyPoint, DOMAIN_PARAMS); | ||||
ParametersWithID params = new ParametersWithID(pubKey,ID.getBytes()); | |||||
ParametersWithID params = new ParametersWithID(pubKey, BytesUtils.toBytes(ID)); | |||||
return verify(data,params,signature); | return verify(data,params,signature); | ||||
} | } | ||||
@@ -6,6 +6,7 @@ import java.io.File; | |||||
import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||
import java.lang.management.ManagementFactory; | import java.lang.management.ManagementFactory; | ||||
import java.net.URL; | import java.net.URL; | ||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.List; | import java.util.List; | ||||
@@ -49,7 +50,7 @@ public class GatewayBooter { | |||||
bootInfos.add(String.format("GW_BOOT_PID = [%s] \r\n", pid)); | bootInfos.add(String.format("GW_BOOT_PID = [%s] \r\n", pid)); | ||||
try (FileOutputStream outputStream = new FileOutputStream(pidFile)) { | try (FileOutputStream outputStream = new FileOutputStream(pidFile)) { | ||||
for (String bootInfo : bootInfos) { | for (String bootInfo : bootInfos) { | ||||
outputStream.write(bootInfo.getBytes()); | |||||
outputStream.write(bootInfo.getBytes(StandardCharsets.UTF_8)); | |||||
} | } | ||||
outputStream.flush(); | outputStream.flush(); | ||||
} | } | ||||
@@ -6,6 +6,7 @@ import java.io.IOException; | |||||
import java.lang.management.ManagementFactory; | import java.lang.management.ManagementFactory; | ||||
import java.lang.reflect.InvocationTargetException; | import java.lang.reflect.InvocationTargetException; | ||||
import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.List; | import java.util.List; | ||||
@@ -70,7 +71,7 @@ public class PeerBooter { | |||||
bootInfos.add(String.format("PEER_BOOT_PID = [%s] \r\n", pid)); | bootInfos.add(String.format("PEER_BOOT_PID = [%s] \r\n", pid)); | ||||
try (FileOutputStream outputStream = new FileOutputStream(pidFile)) { | try (FileOutputStream outputStream = new FileOutputStream(pidFile)) { | ||||
for (String bootInfo : bootInfos) { | for (String bootInfo : bootInfos) { | ||||
outputStream.write(bootInfo.getBytes()); | |||||
outputStream.write(bootInfo.getBytes(StandardCharsets.UTF_8)); | |||||
} | } | ||||
outputStream.flush(); | outputStream.flush(); | ||||
} | } | ||||
@@ -12,7 +12,6 @@ import com.jd.blockchain.ledger.ParticipantNode; | |||||
public class ParticipantCertData implements ParticipantNode { | public class ParticipantCertData implements ParticipantNode { | ||||
private int id; | private int id; | ||||
private String address; | private String address; | ||||
private String name; | private String name; | ||||
private PubKey pubKey; | private PubKey pubKey; | ||||
@@ -21,6 +20,7 @@ public class ParticipantCertData implements ParticipantNode { | |||||
} | } | ||||
public ParticipantCertData(ParticipantNode participantNode) { | public ParticipantCertData(ParticipantNode participantNode) { | ||||
this.id = participantNode.getId(); | |||||
this.address = participantNode.getAddress(); | this.address = participantNode.getAddress(); | ||||
this.name = participantNode.getName(); | this.name = participantNode.getName(); | ||||
this.pubKey = participantNode.getPubKey(); | this.pubKey = participantNode.getPubKey(); | ||||
@@ -31,8 +31,6 @@ public class ParticipantCertData implements ParticipantNode { | |||||
this.name = name; | this.name = name; | ||||
this.pubKey = pubKey; | this.pubKey = pubKey; | ||||
} | } | ||||
@Override | @Override | ||||
public String getAddress() { | public String getAddress() { | ||||
@@ -49,16 +47,11 @@ public class ParticipantCertData implements ParticipantNode { | |||||
return pubKey; | return pubKey; | ||||
} | } | ||||
public int getId() { | public int getId() { | ||||
return id; | return id; | ||||
} | } | ||||
public void setId(int id) { | public void setId(int id) { | ||||
this.id = id; | this.id = id; | ||||
} | } | ||||
} | } |
@@ -253,10 +253,10 @@ public class ContractLedgerContext implements LedgerContext { | |||||
public boolean isJson(String str) { | public boolean isJson(String str) { | ||||
boolean result = false; | boolean result = false; | ||||
try { | try { | ||||
Object obj=JSON.parse(str); | |||||
Object obj = JSON.parse(str); | |||||
result = true; | result = true; | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
result=false; | |||||
result = false; | |||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
@@ -278,10 +278,11 @@ public class ContractLedgerContext implements LedgerContext { | |||||
public DataAccountKVSetOperationBuilder set(String key, String value, long expVersion) { | public DataAccountKVSetOperationBuilder set(String key, String value, long expVersion) { | ||||
BytesValue bytesValue; | BytesValue bytesValue; | ||||
if (isJson(value)) { | if (isJson(value)) { | ||||
bytesValue = new BytesValueEntry(BytesValueType.JSON, value.getBytes()); | |||||
bytesValue = new BytesValueEntry(BytesValueType.JSON, BytesUtils.toBytes(value)); | |||||
} | } | ||||
else { | else { | ||||
bytesValue = new BytesValueEntry(BytesValueType.TEXT, value.getBytes()); | |||||
bytesValue = new BytesValueEntry(BytesValueType.TEXT, BytesUtils.toBytes(value)); | |||||
} | } | ||||
this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); | ||||
generatedOpList.add(op); | generatedOpList.add(op); | ||||
@@ -49,22 +49,22 @@ public class LedgerTestUtils { | |||||
return txHandle.txRequest; | return txHandle.txRequest; | ||||
} | } | ||||
public static TransactionRequest createContractEventTxRequest(HashDigest ledgerHash, | |||||
SignatureFunction signatureFunction, String contractAddress, String event, byte[] args) { | |||||
TxHandle txHandle = new TxHandle(); | |||||
TxTemplate txTemp = new TxTemplate(ledgerHash, txHandle); | |||||
txTemp.contractEvents().send(contractAddress, event, args); | |||||
AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | |||||
PubKey pubKey = cryptoKeyPair.getPubKey(); | |||||
txTemp.users().register(new BlockchainIdentityData(pubKey)); | |||||
PreparedTransaction ptx = txTemp.prepare(); | |||||
ptx.sign(cryptoKeyPair); | |||||
ptx.commit(); | |||||
return txHandle.txRequest; | |||||
} | |||||
// public static TransactionRequest createContractEventTxRequest(HashDigest ledgerHash, | |||||
// SignatureFunction signatureFunction, String contractAddress, String event, byte[] args) { | |||||
// TxHandle txHandle = new TxHandle(); | |||||
// | |||||
// TxTemplate txTemp = new TxTemplate(ledgerHash, txHandle); | |||||
// | |||||
// txTemp.contractEvents().send(contractAddress, event, args); | |||||
// | |||||
// AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | |||||
// PubKey pubKey = cryptoKeyPair.getPubKey(); | |||||
// txTemp.users().register(new BlockchainIdentityData(pubKey)); | |||||
// PreparedTransaction ptx = txTemp.prepare(); | |||||
// ptx.sign(cryptoKeyPair); | |||||
// ptx.commit(); | |||||
// return txHandle.txRequest; | |||||
// } | |||||
public static TransactionStagedSnapshot generateRandomSnapshot() { | public static TransactionStagedSnapshot generateRandomSnapshot() { | ||||
TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); | TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); | ||||
@@ -6,14 +6,12 @@ import static org.junit.Assert.assertNotNull; | |||||
import static org.junit.Assert.assertNull; | import static org.junit.Assert.assertNull; | ||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import java.security.KeyFactory; | |||||
import java.util.Random; | import java.util.Random; | ||||
import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import org.junit.Test; | import org.junit.Test; | ||||
import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | import com.jd.blockchain.ledger.BlockchainKeyGenerator; | ||||
import com.jd.blockchain.ledger.BlockchainKeypair; | import com.jd.blockchain.ledger.BlockchainKeypair; | ||||
import com.jd.blockchain.ledger.ContractCodeDeployOperation; | import com.jd.blockchain.ledger.ContractCodeDeployOperation; | ||||
@@ -25,7 +23,6 @@ import com.jd.blockchain.ledger.DataAccountRegisterOperation; | |||||
import com.jd.blockchain.ledger.DigitalSignature; | import com.jd.blockchain.ledger.DigitalSignature; | ||||
import com.jd.blockchain.ledger.LedgerTransaction; | import com.jd.blockchain.ledger.LedgerTransaction; | ||||
import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
import com.jd.blockchain.ledger.TransactionBuilder; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
import com.jd.blockchain.ledger.TransactionRequestBuilder; | import com.jd.blockchain.ledger.TransactionRequestBuilder; | ||||
import com.jd.blockchain.ledger.TransactionState; | import com.jd.blockchain.ledger.TransactionState; | ||||
@@ -62,7 +59,7 @@ public class TransactionSetTest { | |||||
// Build transaction request; | // Build transaction request; | ||||
HashDigest ledgerHash = LedgerTestUtils.generateRandomHash(); | HashDigest ledgerHash = LedgerTestUtils.generateRandomHash(); | ||||
TransactionBuilder txBuilder = new TxBuilder(ledgerHash); | |||||
TxBuilder txBuilder = new TxBuilder(ledgerHash); | |||||
BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); | BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); | ||||
UserRegisterOperation userRegOp = txBuilder.users().register(userKey.getIdentity()); | UserRegisterOperation userRegOp = txBuilder.users().register(userKey.getIdentity()); | ||||
@@ -0,0 +1,196 @@ | |||||
package com.jd.blockchain.contract; | |||||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
import com.jd.blockchain.ledger.*; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.IllegalDataException; | |||||
import org.springframework.util.ReflectionUtils; | |||||
import java.nio.ByteBuffer; | |||||
import java.util.Arrays; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
/** | |||||
* @author zhaogw | |||||
* date 2019/5/16 18:05 | |||||
*/ | |||||
public class ContractSerializeUtils { | |||||
public static Map<Integer, Class<?>> DATA_CONTRACT_MAP = new HashMap<>(); | |||||
public static final Integer[] PRIMITIVE_DATA_CODES = {DataCodes.CONTRACT_INT8, DataCodes.CONTRACT_INT16, DataCodes.CONTRACT_INT32, | |||||
DataCodes.CONTRACT_INT64, DataCodes.CONTRACT_BIG_INT,DataCodes.CONTRACT_TEXT, DataCodes.CONTRACT_BINARY }; | |||||
/** | |||||
* serialize the Object[] by List<DataContract> list; | |||||
* @param objArr | |||||
* @param dataContractList | |||||
* @return | |||||
*/ | |||||
public static byte[] serializeMethodParam(Object[] objArr,List<DataContract> dataContractList) { | |||||
byte[][] result = new byte[objArr.length][]; | |||||
//将method中形参转换为实体对象,每个形参都必须为@DataContract类型;每个形参使用系统的BinaryProtocol来进行序列化,如果有5个参数,则使用5次序列化; | |||||
int sum = 0; | |||||
for(int i=0;i<objArr.length;i++){ | |||||
DataContract dataContract = dataContractList.get(i); | |||||
objArr[i] = regenObj(dataContract,objArr[i]); | |||||
//get data interface; | |||||
result[i] = BinaryProtocol.encode(objArr[i],getDataIntf().get(dataContract.code())); | |||||
sum += result[i].length; | |||||
} | |||||
/** | |||||
* return byte[] format: | |||||
return is byte[], but now is byte[][], so we should reduct dimension by adding the header info to the rtnBytes[]; | |||||
rtnBytes[]=classTypes.length/first length/second length/third length/result[0]/result[1]/result[2]; | |||||
rtnBytes[0]: 4 bytes(classTypes.length); | |||||
rtnBytes[1]: 4 bytes(1 param's length); | |||||
rtnBytes[2]: 4 bytes(2 param's length); | |||||
rtnBytes[3]: 4 bytes(3 param's length); | |||||
rtnBytes[...]: result[0][] bytes(1 param's length); | |||||
rtnBytes[...]: result[1][] bytes(2 param's length); | |||||
rtnBytes[...]: result[2][] bytes(3 param's length); | |||||
*/ | |||||
int bodyFirstPosition = 4 + 4 * (objArr.length); | |||||
ByteBuffer byteBuffer = ByteBuffer.allocate(bodyFirstPosition + sum); | |||||
byteBuffer.putInt(objArr.length); | |||||
for(int j=0; j<result.length; j++) { | |||||
byte[] curResult = result[j]; | |||||
byteBuffer.putInt(curResult.length); | |||||
} | |||||
for(int k=0; k<result.length; k++){ | |||||
byteBuffer.put(result[k]); | |||||
} | |||||
return byteBuffer.array(); | |||||
} | |||||
/** | |||||
* deserialize the params bytes[]; | |||||
* params format: nums|first length| second length| third length| ... |bytes[0]| byte[1] | bytes[2]| ... | |||||
* @param params | |||||
* @param dataContractList | |||||
* @return | |||||
*/ | |||||
public static Object[] deserializeMethodParam(byte[] params, List<DataContract> dataContractList) { | |||||
Object result[] = new Object[dataContractList.size()]; | |||||
ByteBuffer byteBuffer = ByteBuffer.allocate(params.length); | |||||
byteBuffer.put(params); | |||||
int paramNums = byteBuffer.getInt(0); | |||||
if(paramNums != dataContractList.size()){ | |||||
throw new IllegalArgumentException("deserialize Method param. params'length in byte[] != method's param length"); | |||||
} | |||||
int offsetPosition = (1 + dataContractList.size())*4; //start position of real data; | |||||
for(int i=0; i<dataContractList.size(); i++){ | |||||
DataContract dataContract = dataContractList.get(i); | |||||
int curParamLength = byteBuffer.getInt((i+1)*4); | |||||
ByteBuffer byteBuffer1 = ByteBuffer.allocate(curParamLength); | |||||
byteBuffer1.put(params,offsetPosition,curParamLength); | |||||
offsetPosition += curParamLength; | |||||
//if dataContract=primitive type(byte/short/int/long/String),only use its getValues(); | |||||
Object object = BinaryProtocol.decodeAs(byteBuffer1.array(), | |||||
getDataIntf().get(dataContract.code())); | |||||
if(isPrimitiveType(dataContract.code())){ | |||||
Class<?> classObj = getDataIntf().get(dataContract.code()); | |||||
try { | |||||
result[i] = ReflectionUtils.invokeMethod(classObj.getMethod("getValue"),object); | |||||
} catch (NoSuchMethodException e) { | |||||
throw new IllegalStateException("no getValue(). detail="+e.getMessage()); | |||||
} | |||||
}else { | |||||
result[i] = object; | |||||
} | |||||
byteBuffer1.clear(); | |||||
} | |||||
return result; | |||||
} | |||||
/** | |||||
* the param types that we can support; | |||||
* @param <T> | |||||
* @return | |||||
*/ | |||||
public static <T> Map<Integer, Class<?> > getDataIntf(){ | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT8, CONTRACT_INT8.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT16, CONTRACT_INT16.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT32, CONTRACT_INT32.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT64, CONTRACT_INT64.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_TEXT, CONTRACT_TEXT.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BINARY, CONTRACT_BINARY.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BIG_INT, CONTRACT_BIG_INT.class); | |||||
DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BIZ_CONTENT, ContractBizContent.class); | |||||
return DATA_CONTRACT_MAP; | |||||
} | |||||
public static boolean isPrimitiveType(int dataContractCode){ | |||||
return Arrays.asList(PRIMITIVE_DATA_CODES).contains(dataContractCode); | |||||
} | |||||
private static Object regenObj(DataContract dataContract, Object object){ | |||||
if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT8.class)){ | |||||
return (CONTRACT_INT8) () -> Byte.parseByte(object.toString()); | |||||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT16.class)){ | |||||
return (CONTRACT_INT16) () -> Short.parseShort(object.toString()); | |||||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT32.class)){ | |||||
return (CONTRACT_INT32) () -> Integer.parseInt(object.toString()); | |||||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT64.class)){ | |||||
return (CONTRACT_INT64) () -> Long.parseLong(object.toString()); | |||||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_TEXT.class)){ | |||||
return (CONTRACT_TEXT) () -> object.toString(); | |||||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_BINARY.class)){ | |||||
return (CONTRACT_BINARY) () -> (Bytes) object; | |||||
}else if(getDataIntf().get(dataContract.code()).equals(ContractBizContent.class)){ | |||||
ContractBizContent contractBizContent = (ContractBizContent)object; | |||||
return contractBizContent; | |||||
}else { | |||||
throw new IllegalDataException("cann't get new Object by dataContract and object."); | |||||
} | |||||
} | |||||
/** | |||||
* get contractType(contain @DataContract) by primitive class type; | |||||
* some class type can be supported default (byte/char/int/long/String/Bytes, and so on). | |||||
* in other words, need not contain the @DataContract in its class for contract param's serialization or deserialization. | |||||
* @param classType | |||||
* @return | |||||
*/ | |||||
private static Class<?> getContractTypeByPrimitiveType(Class<?> classType) { | |||||
if(classType.equals(byte.class) || classType.equals(Byte.class)){ | |||||
return CONTRACT_INT8.class; | |||||
}else if(classType.equals(char.class) || classType.equals(Character.class)){ | |||||
return CONTRACT_INT16.class; | |||||
}else if(classType.equals(int.class) || classType.equals(Integer.class)){ | |||||
return CONTRACT_INT32.class; | |||||
}else if(classType.equals(long.class) || classType.equals(Long.class)){ | |||||
return CONTRACT_INT64.class; | |||||
}else if(classType.equals(String.class)){ | |||||
return CONTRACT_TEXT.class; | |||||
}else if(classType.equals(Bytes.class)){ | |||||
return CONTRACT_BINARY.class; | |||||
}else { | |||||
throw new IllegalDataException(String.format("no support the classType=%s, please check @DataContract.",classType.toString())); | |||||
} | |||||
} | |||||
public static DataContract parseDataContract(Class<?> classType){ | |||||
DataContract dataContract = classType.getAnnotation(DataContract.class); | |||||
//if the param's class Type don't contain @DataContract, then check parameterAnnotations of this method. | |||||
if(dataContract == null){ | |||||
boolean canPass = false; | |||||
//if parameterAnnotations don't contain @DataContract, is it primitive type? | |||||
Class<?> contractType = getContractTypeByPrimitiveType(classType); | |||||
dataContract = contractType.getAnnotation(DataContract.class); | |||||
} | |||||
if(!getDataIntf().containsKey(dataContract.code())){ | |||||
throw new IllegalArgumentException(String.format( | |||||
"for now, this @dataContract(code=%s) is forbidden in the param list.",dataContract.code())); | |||||
} | |||||
return dataContract; | |||||
} | |||||
} |
@@ -0,0 +1,20 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
import java.math.BigDecimal; | |||||
/** | |||||
* contract args for BIG_INT; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_BIG_INT) | |||||
public interface CONTRACT_BIG_INT { | |||||
@DataField(order=2, primitiveType= PrimitiveType.BIG_INT) | |||||
BigDecimal getValue(); | |||||
} |
@@ -0,0 +1,19 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
/** | |||||
* contract args for Binary; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_BINARY) | |||||
public interface CONTRACT_BINARY { | |||||
@DataField(order=2, primitiveType= PrimitiveType.BYTES) | |||||
Bytes getValue(); | |||||
} |
@@ -0,0 +1,18 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
/** | |||||
* contract args for int16; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_INT16) | |||||
public interface CONTRACT_INT16 { | |||||
@DataField(order=2, primitiveType= PrimitiveType.INT16) | |||||
short getValue(); | |||||
} |
@@ -0,0 +1,18 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
/** | |||||
* contract args for int32; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_INT32) | |||||
public interface CONTRACT_INT32 { | |||||
@DataField(order=2, primitiveType= PrimitiveType.INT32) | |||||
int getValue(); | |||||
} |
@@ -0,0 +1,18 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
/** | |||||
* contract args for int64; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_INT64) | |||||
public interface CONTRACT_INT64 { | |||||
@DataField(order=2, primitiveType= PrimitiveType.INT64) | |||||
long getValue(); | |||||
} |
@@ -0,0 +1,18 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
/** | |||||
* contract args for int8; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_INT8) | |||||
public interface CONTRACT_INT8 { | |||||
@DataField(order=2, primitiveType= PrimitiveType.INT8) | |||||
Byte getValue(); | |||||
} |
@@ -0,0 +1,18 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
/** | |||||
* contract args for String; | |||||
* @author zhaogw | |||||
* date 2019-05-17 15:32 | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_TEXT) | |||||
public interface CONTRACT_TEXT { | |||||
@DataField(order=2, primitiveType= PrimitiveType.TEXT) | |||||
String getValue(); | |||||
} |
@@ -0,0 +1,20 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
/** | |||||
* build complex param Object, provide more String attributes; | |||||
*/ | |||||
@DataContract(code = DataCodes.CONTRACT_BIZ_CONTENT) | |||||
public interface ContractBizContent { | |||||
/** | |||||
* param lists; | |||||
* @return | |||||
*/ | |||||
@DataField(order = 1, list = true, primitiveType = PrimitiveType.TEXT, genericContract = true) | |||||
String[] getAttrs(); | |||||
} |
@@ -31,7 +31,6 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
private static final ContractEventSendOperationBuilderImpl CONTRACT_EVENT_SEND_OP_BUILDER = new ContractEventSendOperationBuilderImpl(); | private static final ContractEventSendOperationBuilderImpl CONTRACT_EVENT_SEND_OP_BUILDER = new ContractEventSendOperationBuilderImpl(); | ||||
private LedgerInitOperationBuilder ledgerInitOpBuilder = new LedgerInitOperationBuilderFilter(); | private LedgerInitOperationBuilder ledgerInitOpBuilder = new LedgerInitOperationBuilderFilter(); | ||||
private UserRegisterOperationBuilder userRegOpBuilder = new UserRegisterOperationBuilderFilter(); | private UserRegisterOperationBuilder userRegOpBuilder = new UserRegisterOperationBuilderFilter(); | ||||
@@ -42,6 +41,8 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
private ContractEventSendOperationBuilder contractEventSendOpBuilder = new ContractEventSendOperationBuilderFilter(); | private ContractEventSendOperationBuilder contractEventSendOpBuilder = new ContractEventSendOperationBuilderFilter(); | ||||
private ContractInvocationProxyBuilder contractInvoProxyBuilder = new ContractInvocationProxyBuilder(); | |||||
private List<Operation> operationList = new ArrayList<>(); | private List<Operation> operationList = new ArrayList<>(); | ||||
@Override | @Override | ||||
@@ -74,15 +75,18 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
return contractCodeDeployOpBuilder; | return contractCodeDeployOpBuilder; | ||||
} | } | ||||
@Override | |||||
public ContractEventSendOperationBuilder contractEvents() { | public ContractEventSendOperationBuilder contractEvents() { | ||||
return contractEventSendOpBuilder; | return contractEventSendOpBuilder; | ||||
} | } | ||||
@Override | @Override | ||||
public <T> T contract(String address, Class<T> contractIntf) { | public <T> T contract(String address, Class<T> contractIntf) { | ||||
// TODO Auto-generated method stub | |||||
return null; | |||||
return contractInvoProxyBuilder.create(address, contractIntf, contractEventSendOpBuilder); | |||||
} | |||||
@Override | |||||
public <T> T contract(Bytes address, Class<T> contractIntf) { | |||||
return contractInvoProxyBuilder.create(address, contractIntf, contractEventSendOpBuilder); | |||||
} | } | ||||
public Collection<Operation> getOperations() { | public Collection<Operation> getOperations() { | ||||
@@ -153,6 +157,7 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
} | } | ||||
return this; | return this; | ||||
} | } | ||||
@Override | @Override | ||||
public DataAccountKVSetOperationBuilder set(String key, String value, long expVersion) { | public DataAccountKVSetOperationBuilder set(String key, String value, long expVersion) { | ||||
innerBuilder.set(key, value, expVersion); | innerBuilder.set(key, value, expVersion); | ||||
@@ -162,6 +167,7 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
} | } | ||||
return this; | return this; | ||||
} | } | ||||
@Override | @Override | ||||
public DataAccountKVSetOperationBuilder set(String key, long value, long expVersion) { | public DataAccountKVSetOperationBuilder set(String key, long value, long expVersion) { | ||||
innerBuilder.set(key, value, expVersion); | innerBuilder.set(key, value, expVersion); | ||||
@@ -171,6 +177,7 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
} | } | ||||
return this; | return this; | ||||
} | } | ||||
@Override | @Override | ||||
public DataAccountKVSetOperationBuilder set(String key, Bytes value, long expVersion) { | public DataAccountKVSetOperationBuilder set(String key, Bytes value, long expVersion) { | ||||
innerBuilder.set(key, value, expVersion); | innerBuilder.set(key, value, expVersion); | ||||
@@ -1,7 +1,8 @@ | |||||
package com.jd.blockchain.transaction; | package com.jd.blockchain.transaction; | ||||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||||
import com.jd.blockchain.utils.IllegalDataException; | |||||
import java.lang.reflect.InvocationHandler; | import java.lang.reflect.InvocationHandler; | ||||
import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
@@ -19,17 +20,15 @@ public class ContractInvocationProxy implements InvocationHandler { | |||||
public ContractInvocationProxy(Bytes contractAddress, ContractType contractType, | public ContractInvocationProxy(Bytes contractAddress, ContractType contractType, | ||||
ContractEventSendOperationBuilder sendOpBuilder) { | ContractEventSendOperationBuilder sendOpBuilder) { | ||||
this.contractAddress = contractAddress; | this.contractAddress = contractAddress; | ||||
if(contractType == null){ | |||||
throw new IllegalDataException("contractType == null, no invoke really."); | |||||
} | |||||
this.contractType = contractType; | this.contractType = contractType; | ||||
this.sendOpBuilder = sendOpBuilder; | this.sendOpBuilder = sendOpBuilder; | ||||
} | } | ||||
@Override | @Override | ||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | ||||
if(contractType == null){ | |||||
return "contractType == null, no invoke really."; | |||||
} | |||||
String event = contractType.getEvent(method); | String event = contractType.getEvent(method); | ||||
if (event == null) { | if (event == null) { | ||||
// 适配 Object 对象的方法; | // 适配 Object 对象的方法; | ||||
@@ -39,15 +38,17 @@ public class ContractInvocationProxy implements InvocationHandler { | |||||
// hashCode 方法; | // hashCode 方法; | ||||
} | } | ||||
// 合约方法; | // 合约方法; | ||||
byte[] argBytes = serializeArgs(args); | |||||
byte[] argBytes = serializeArgs(args,method); | |||||
sendOpBuilder.send(contractAddress, event, argBytes); | sendOpBuilder.send(contractAddress, event, argBytes); | ||||
// TODO: 暂时未考虑有返回值的情况; | // TODO: 暂时未考虑有返回值的情况; | ||||
return null; | return null; | ||||
} | } | ||||
private byte[] serializeArgs(Object[] args) { | |||||
// TODO 根据方法参数的定义序列化参数; | |||||
return BinarySerializeUtils.serialize(args); | |||||
private byte[] serializeArgs(Object[] args, Method method) { | |||||
if(args == null || args.length==0){ | |||||
return null; | |||||
} | |||||
return ContractSerializeUtils.serializeMethodParam(args,contractType.getDataContractMap().get(method)); | |||||
} | } | ||||
} | } |
@@ -1,6 +1,58 @@ | |||||
package com.jd.blockchain.transaction; | package com.jd.blockchain.transaction; | ||||
class ContractInvocationProxyBuilder { | |||||
import com.jd.blockchain.contract.Contract; | |||||
import com.jd.blockchain.contract.ContractEvent; | |||||
import com.jd.blockchain.utils.BaseConstant; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.IllegalDataException; | |||||
import java.lang.annotation.Annotation; | |||||
import java.lang.reflect.Method; | |||||
import java.lang.reflect.Proxy; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
public class ContractInvocationProxyBuilder { | |||||
private Map<Class<?>, ContractType> contractTypes = new ConcurrentHashMap<>(); | |||||
public <T> T create(String address, Class<T> contractIntf, ContractEventSendOperationBuilder contractEventBuilder) { | |||||
return create(Bytes.fromBase58(address), contractIntf, contractEventBuilder); | |||||
} | |||||
@SuppressWarnings("unchecked") | |||||
public <T> T create(Bytes address, Class<T> contractIntf, ContractEventSendOperationBuilder contractEventBuilder) { | |||||
ContractType contractType = resolveContractType(contractIntf); | |||||
ContractInvocationProxy proxyHandler = new ContractInvocationProxy(address, contractType, | |||||
contractEventBuilder); | |||||
T proxy = (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), | |||||
new Class<?>[] { contractIntf }, proxyHandler); | |||||
return (T) proxy; | |||||
} | |||||
private ContractType resolveContractType(Class<?> contractIntf) { | |||||
ContractType contractType = contractTypes.get(contractIntf); | |||||
if (contractType != null) { | |||||
return contractType; | |||||
} | |||||
// TODO 检查返回值类型; | |||||
ContractType contractType1 = ContractType.resolve(contractIntf); | |||||
contractTypes.put(contractIntf,contractType1); | |||||
return contractType1; | |||||
} | |||||
/** | |||||
* is contractType really? identified by @Contract; | |||||
* @param contractIntf | |||||
* @return | |||||
*/ | |||||
private boolean isContractType(Class<?> contractIntf) { | |||||
Annotation annotation = contractIntf.getDeclaredAnnotation(Contract.class); | |||||
return annotation != null ? true : false; | |||||
} | |||||
} | } |
@@ -1,26 +1,36 @@ | |||||
package com.jd.blockchain.transaction; | package com.jd.blockchain.transaction; | ||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.contract.Contract; | |||||
import com.jd.blockchain.contract.ContractEvent; | |||||
import com.jd.blockchain.contract.ContractException; | |||||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||||
import com.jd.blockchain.utils.IllegalDataException; | |||||
import java.lang.annotation.Annotation; | |||||
import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
import java.util.Set; | |||||
import java.util.SortedMap; | |||||
import java.util.*; | |||||
public class ContractType { | |||||
class ContractType { | |||||
private String name; | private String name; | ||||
private SortedMap<String, Method> events; | |||||
private SortedMap<Method, String> handleMethods; | |||||
private Map<String, Method> events = new HashMap<>(); | |||||
private Map<Method, String> handleMethods = new HashMap<>(); | |||||
private Map<Method, List<DataContract>> dataContractMap = new HashMap<>(); | |||||
/** | /** | ||||
* 返回声明的所有事件; | * 返回声明的所有事件; | ||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
Set<String> getEvents() { | |||||
public Set<String> getEvents() { | |||||
return events.keySet(); | return events.keySet(); | ||||
} | } | ||||
public Map<Method, List<DataContract>> getDataContractMap() { | |||||
return dataContractMap; | |||||
} | |||||
/** | /** | ||||
* 返回指定方法声明的事件;<br> | * 返回指定方法声明的事件;<br> | ||||
* | * | ||||
@@ -29,7 +39,7 @@ class ContractType { | |||||
* @param method | * @param method | ||||
* @return | * @return | ||||
*/ | */ | ||||
String getEvent(Method method) { | |||||
public String getEvent(Method method) { | |||||
return handleMethods.get(method); | return handleMethods.get(method); | ||||
} | } | ||||
@@ -41,16 +51,59 @@ class ContractType { | |||||
* @param event | * @param event | ||||
* @return | * @return | ||||
*/ | */ | ||||
Method getHandleMethod(String event) { | |||||
public Method getHandleMethod(String event) { | |||||
return events.get(event); | return events.get(event); | ||||
} | } | ||||
private ContractType() { | private ContractType() { | ||||
} | } | ||||
// public static ContractType resolve(Class<?> contractIntf) { | |||||
// | |||||
// } | |||||
public static ContractType resolve(Class<?> contractIntf){ | |||||
ContractType contractType = new ContractType(); | |||||
Annotation annotation = contractIntf.getDeclaredAnnotation(Contract.class); | |||||
//contains: @Contract? | |||||
boolean isContractType = annotation != null ? true : false; | |||||
if(!isContractType){ | |||||
throw new IllegalDataException("is not Contract Type, becaust there is not @Contract."); | |||||
} | |||||
//contractIntf contains @Contract and @ContractEvent; | |||||
Method[] classMethods = contractIntf.getDeclaredMethods(); | |||||
for (Method method : classMethods) { | |||||
// if current method contains @ContractEvent,then put it in this map; | |||||
ContractEvent contractEvent = method.getAnnotation(ContractEvent.class); | |||||
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_)){ | |||||
throw new ContractException("there is repeat definition of contractEvent to @ContractEvent."); | |||||
} | |||||
//check param's type is fit for need. | |||||
Class<?>[] paramTypes = method.getParameterTypes(); | |||||
List dataContractList = new ArrayList(); | |||||
for(Class<?> curParamType : paramTypes){ | |||||
DataContract dataContract = ContractSerializeUtils.parseDataContract(curParamType); | |||||
dataContractList.add(dataContract); | |||||
} | |||||
if(dataContractList.size()>0){ | |||||
contractType.dataContractMap.put(method,dataContractList); | |||||
} | |||||
contractType.events.put(eventName_, method); | |||||
contractType.handleMethods.put(method,eventName_); | |||||
} | |||||
} | |||||
return contractType; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "ContractType{" + | |||||
"name='" + name + '\'' + | |||||
", events=" + events + | |||||
", handleMethods=" + handleMethods + | |||||
'}'; | |||||
} | |||||
} | } |
@@ -1,14 +1,25 @@ | |||||
package com.jd.blockchain.transaction; | package com.jd.blockchain.transaction; | ||||
import com.jd.blockchain.utils.Bytes; | |||||
public interface EventOperator { | public interface EventOperator { | ||||
// /** | |||||
// * 合约事件; | |||||
// * | |||||
// * @return | |||||
// */ | |||||
// @Deprecated | |||||
// ContractEventSendOperationBuilder contractEvents(); | |||||
/** | /** | ||||
* 部署合约; | |||||
* 创建调用合约的代理实例; | |||||
* | * | ||||
* @param address | |||||
* @param contractIntf | |||||
* @return | * @return | ||||
*/ | */ | ||||
@Deprecated | |||||
ContractEventSendOperationBuilder contractEvents(); | |||||
<T> T contract(String address, Class<T> contractIntf); | |||||
/** | /** | ||||
* 创建调用合约的代理实例; | * 创建调用合约的代理实例; | ||||
@@ -17,6 +28,6 @@ public interface EventOperator { | |||||
* @param contractIntf | * @param contractIntf | ||||
* @return | * @return | ||||
*/ | */ | ||||
<T> T contract(String address, Class<T> contractIntf); | |||||
<T> T contract(Bytes address, Class<T> contractIntf); | |||||
} | } |
@@ -19,7 +19,7 @@ public class TxBuilder implements TransactionBuilder { | |||||
private BlockchainOperationFactory opFactory = new BlockchainOperationFactory(); | private BlockchainOperationFactory opFactory = new BlockchainOperationFactory(); | ||||
private static final String DEFAULT_HASH_ALGORITHM = "SHA256"; | private static final String DEFAULT_HASH_ALGORITHM = "SHA256"; | ||||
private HashDigest ledgerHash; | private HashDigest ledgerHash; | ||||
public TxBuilder(HashDigest ledgerHash) { | public TxBuilder(HashDigest ledgerHash) { | ||||
@@ -36,16 +36,16 @@ public class TxBuilder implements TransactionBuilder { | |||||
TransactionContent txContent = prepareContent(); | TransactionContent txContent = prepareContent(); | ||||
return new TxRequestBuilder(txContent); | return new TxRequestBuilder(txContent); | ||||
} | } | ||||
@Override | @Override | ||||
public TransactionContent prepareContent() { | public TransactionContent prepareContent() { | ||||
TxContentBlob txContent = new TxContentBlob(ledgerHash); | TxContentBlob txContent = new TxContentBlob(ledgerHash); | ||||
txContent.addOperations(opFactory.getOperations()); | txContent.addOperations(opFactory.getOperations()); | ||||
byte[] contentBodyBytes = BinaryProtocol.encode(txContent, TransactionContentBody.class); | byte[] contentBodyBytes = BinaryProtocol.encode(txContent, TransactionContentBody.class); | ||||
HashDigest contentHash = Crypto.getHashFunction(DEFAULT_HASH_ALGORITHM).hash(contentBodyBytes); | HashDigest contentHash = Crypto.getHashFunction(DEFAULT_HASH_ALGORITHM).hash(contentBodyBytes); | ||||
txContent.setHash(contentHash); | txContent.setHash(contentHash); | ||||
return txContent; | return txContent; | ||||
} | } | ||||
@@ -53,7 +53,7 @@ public class TxBuilder implements TransactionBuilder { | |||||
public LedgerInitOperationBuilder ledgers() { | public LedgerInitOperationBuilder ledgers() { | ||||
return opFactory.ledgers(); | return opFactory.ledgers(); | ||||
} | } | ||||
@Override | @Override | ||||
public UserRegisterOperationBuilder users() { | public UserRegisterOperationBuilder users() { | ||||
return opFactory.users(); | return opFactory.users(); | ||||
@@ -63,7 +63,7 @@ public class TxBuilder implements TransactionBuilder { | |||||
public DataAccountRegisterOperationBuilder dataAccounts() { | public DataAccountRegisterOperationBuilder dataAccounts() { | ||||
return opFactory.dataAccounts(); | return opFactory.dataAccounts(); | ||||
} | } | ||||
@Override | @Override | ||||
public DataAccountKVSetOperationBuilder dataAccount(String accountAddress) { | public DataAccountKVSetOperationBuilder dataAccount(String accountAddress) { | ||||
return opFactory.dataAccount(accountAddress); | return opFactory.dataAccount(accountAddress); | ||||
@@ -79,15 +79,18 @@ public class TxBuilder implements TransactionBuilder { | |||||
return opFactory.contracts(); | return opFactory.contracts(); | ||||
} | } | ||||
@Override | |||||
public ContractEventSendOperationBuilder contractEvents() { | public ContractEventSendOperationBuilder contractEvents() { | ||||
return opFactory.contractEvents(); | return opFactory.contractEvents(); | ||||
} | } | ||||
@Override | |||||
public <T> T contract(Bytes address, Class<T> contractIntf) { | |||||
return opFactory.contract(address, contractIntf); | |||||
} | |||||
@Override | @Override | ||||
public <T> T contract(String address, Class<T> contractIntf) { | public <T> T contract(String address, Class<T> contractIntf) { | ||||
// TODO Auto-generated method stub | |||||
throw new IllegalStateException("Not implemented."); | |||||
return opFactory.contract(address, contractIntf); | |||||
} | } | ||||
} | } |
@@ -53,9 +53,14 @@ public class TxTemplate implements TransactionTemplate { | |||||
return txBuilder.contracts(); | return txBuilder.contracts(); | ||||
} | } | ||||
// @Override | |||||
// public ContractEventSendOperationBuilder contractEvents() { | |||||
// return txBuilder.contractEvents(); | |||||
// } | |||||
@Override | @Override | ||||
public ContractEventSendOperationBuilder contractEvents() { | |||||
return txBuilder.contractEvents(); | |||||
public <T> T contract(Bytes address, Class<T> contractIntf) { | |||||
return txBuilder.contract(address, contractIntf); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -1,6 +1,6 @@ | |||||
//package test.com.jd.blockchain.peer.web; | //package test.com.jd.blockchain.peer.web; | ||||
// | // | ||||
//import org.springframework.boot.test.mock.mockito.MockBean; | |||||
//import org.springframework.boot.test.mocker.mockito.MockBean; | |||||
//import org.springframework.context.annotation.Bean; | //import org.springframework.context.annotation.Bean; | ||||
//import org.springframework.context.annotation.Configuration; | //import org.springframework.context.annotation.Configuration; | ||||
// | // | ||||
@@ -2,7 +2,7 @@ | |||||
// | // | ||||
//import static org.mockito.Matchers.any; | //import static org.mockito.Matchers.any; | ||||
//import static org.mockito.Matchers.anyInt; | //import static org.mockito.Matchers.anyInt; | ||||
//import static org.mockito.Mockito.mock; | |||||
//import static org.mockito.Mockito.mocker; | |||||
//import static org.mockito.Mockito.spy; | //import static org.mockito.Mockito.spy; | ||||
//import static org.mockito.Mockito.when; | //import static org.mockito.Mockito.when; | ||||
// | // | ||||
@@ -211,7 +211,7 @@ | |||||
// //when(keystoreService.getBlockchainKey(key.getAddress())).thenReturn(keyInfo); | // //when(keystoreService.getBlockchainKey(key.getAddress())).thenReturn(keyInfo); | ||||
// //when(keystoreService.sign(any(), key.getAddress())).then(answerSignature(key)); | // //when(keystoreService.sign(any(), key.getAddress())).then(answerSignature(key)); | ||||
// | // | ||||
// msgBroadcaster = mock(MessageBroadcaster.class); | |||||
// msgBroadcaster = mocker(MessageBroadcaster.class); | |||||
// | // | ||||
// LedgerInitializingController ctrl = new LedgerInitializingController(peerSettings, keystoreService, | // LedgerInitializingController ctrl = new LedgerInitializingController(peerSettings, keystoreService, | ||||
// ledgerService, msgBroadcaster); | // ledgerService, msgBroadcaster); | ||||
@@ -35,8 +35,8 @@ public class BlockchainServiceProxyTest { | |||||
// | // | ||||
// ArgCaptorMatcher<TransactionRequest> txReqCaptor = new ArgCaptorMatcher<>(); | // ArgCaptorMatcher<TransactionRequest> txReqCaptor = new ArgCaptorMatcher<>(); | ||||
// | // | ||||
// TransactionService consensusService = Mockito.mock(TransactionService.class); | |||||
// BlockchainQueryService queryService = Mockito.mock(BlockchainQueryService.class); | |||||
// TransactionService consensusService = Mockito.mocker(TransactionService.class); | |||||
// BlockchainQueryService queryService = Mockito.mocker(BlockchainQueryService.class); | |||||
// | // | ||||
// HashDigest txContentHash =CryptoUtils.hash(CryptoAlgorithm.SHA_256).hash(UUID.randomUUID().toString().getBytes("UTF-8")); | // HashDigest txContentHash =CryptoUtils.hash(CryptoAlgorithm.SHA_256).hash(UUID.randomUUID().toString().getBytes("UTF-8")); | ||||
// TxResponseMessage expectedResponse = new TxResponseMessage(txContentHash); | // TxResponseMessage expectedResponse = new TxResponseMessage(txContentHash); | ||||
@@ -0,0 +1,281 @@ | |||||
/** | |||||
* Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
* FileName: com.jd.blockchain.sdk.client.ClientOperationUtil | |||||
* Author: shaozhuguang | |||||
* Department: Y事业部 | |||||
* Date: 2019/3/27 下午4:12 | |||||
* Description: | |||||
*/ | |||||
package com.jd.blockchain.sdk.client; | |||||
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.*; | |||||
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.BytesSlice; | |||||
import com.jd.blockchain.utils.io.BytesUtils; | |||||
import org.apache.commons.codec.binary.Base64; | |||||
import java.lang.reflect.Field; | |||||
/** | |||||
* | |||||
* @author shaozhuguang | |||||
* @create 2019/3/27 | |||||
* @since 1.0.0 | |||||
*/ | |||||
public class ClientOperationUtil { | |||||
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) { | |||||
BytesValueType dataType = bytesValue.getType(); | |||||
BytesSlice saveVal = bytesValue.getValue(); | |||||
Object showVal; | |||||
switch (dataType) { | |||||
case BYTES: | |||||
// return hex | |||||
showVal = HexUtils.encode(saveVal.getBytesCopy()); | |||||
break; | |||||
case TEXT: | |||||
case JSON: | |||||
showVal = saveVal.getString(); | |||||
break; | |||||
case INT64: | |||||
showVal = saveVal.getLong(); | |||||
break; | |||||
default: | |||||
showVal = HexUtils.encode(saveVal.getBytesCopy()); | |||||
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 <writeSetObj.size(); i++) { | |||||
JSONObject currWriteSetObj = writeSetObj.getJSONObject(i); | |||||
long expectedVersion = currWriteSetObj.getLong("expectedVersion"); | |||||
JSONObject valueObj = currWriteSetObj.getJSONObject("value"); | |||||
String typeStr = valueObj.getString("type"); | |||||
String realValBase58 = valueObj.getString("value"); | |||||
String key = currWriteSetObj.getString("key"); | |||||
BytesValueType dataType = BytesValueType.valueOf(typeStr); | |||||
BytesValue bytesValue = new BytesValueEntry(dataType, Base58Utils.decode(realValBase58)); | |||||
KVData kvData = new KVData(key, bytesValue, expectedVersion); | |||||
kvOperation.set(kvData); | |||||
} | |||||
return kvOperation; | |||||
} | |||||
public static LedgerInitOperation convertLedgerInitOperation(JSONObject jsonObject) { | |||||
JSONObject legerInitObj = jsonObject.getJSONObject("initSetting"); | |||||
LedgerInitSettingData ledgerInitSettingData = new LedgerInitSettingData(); | |||||
String ledgerSeedStr = legerInitObj.getString("ledgerSeed"); | |||||
// 种子需要做Base64转换 | |||||
ledgerInitSettingData.setLedgerSeed(Base64.decodeBase64(BytesUtils.toBytes(ledgerSeedStr))); | |||||
String consensusProvider = legerInitObj.getString("consensusProvider"); | |||||
ledgerInitSettingData.setConsensusProvider(consensusProvider); | |||||
JSONObject cryptoSettingObj = legerInitObj.getJSONObject("cryptoSetting"); | |||||
boolean autoVerifyHash = cryptoSettingObj.getBoolean("autoVerifyHash"); | |||||
short hashAlgorithm = cryptoSettingObj.getShort("hashAlgorithm"); | |||||
CryptoConfig cryptoConfig = new CryptoConfig(); | |||||
cryptoConfig.setAutoVerifyHash(autoVerifyHash); | |||||
cryptoConfig.setHashAlgorithm(hashAlgorithm); | |||||
ledgerInitSettingData.setCryptoSetting(cryptoConfig); | |||||
JSONObject consensusSettingsObj = legerInitObj.getJSONObject("consensusSettings"); | |||||
Bytes consensusSettings = Bytes.fromBase58(consensusSettingsObj.getString("value")); | |||||
ledgerInitSettingData.setConsensusSettings(consensusSettings); | |||||
JSONArray consensusParticipantsArray = legerInitObj.getJSONArray("consensusParticipants"); | |||||
if (!consensusParticipantsArray.isEmpty()) { | |||||
ParticipantNode[] participantNodes = new ParticipantNode[consensusParticipantsArray.size()]; | |||||
for (int i = 0; i < consensusParticipantsArray.size(); i++) { | |||||
JSONObject currConsensusParticipant = consensusParticipantsArray.getJSONObject(i); | |||||
String addressBase58 = currConsensusParticipant.getString("address"); | |||||
String name = currConsensusParticipant.getString("name"); | |||||
int id = currConsensusParticipant.getInteger("id"); | |||||
JSONObject pubKeyObj = currConsensusParticipant.getJSONObject("pubKey"); | |||||
String pubKeyBase58 = pubKeyObj.getString("value"); | |||||
// 生成ParticipantNode对象 | |||||
ParticipantCertData participantCertData = new ParticipantCertData(id, addressBase58, name, new PubKey(Bytes.fromBase58(pubKeyBase58).toBytes())); | |||||
participantNodes[i] = participantCertData; | |||||
} | |||||
ledgerInitSettingData.setConsensusParticipants(participantNodes); | |||||
} | |||||
return new LedgerInitOpTemplate(ledgerInitSettingData); | |||||
} | |||||
public static UserRegisterOperation convertUserRegisterOperation(JSONObject jsonObject) { | |||||
JSONObject user = jsonObject.getJSONObject("userID"); | |||||
return new UserRegisterOpTemplate(blockchainIdentity(user)); | |||||
} | |||||
public static ContractCodeDeployOperation convertContractCodeDeployOperation(JSONObject jsonObject) { | |||||
JSONObject contract = jsonObject.getJSONObject("contractID"); | |||||
BlockchainIdentityData blockchainIdentity = blockchainIdentity(contract); | |||||
String chainCodeStr = jsonObject.getString("chainCode"); | |||||
ContractCodeDeployOpTemplate contractCodeDeployOpTemplate = new ContractCodeDeployOpTemplate(blockchainIdentity, BytesUtils.toBytes(chainCodeStr)); | |||||
return contractCodeDeployOpTemplate; | |||||
} | |||||
public static ContractEventSendOperation convertContractEventSendOperation(JSONObject jsonObject) { | |||||
JSONObject contractAddressObj = jsonObject.getJSONObject("contractAddress"); | |||||
String contractAddress = contractAddressObj.getString("value"); | |||||
String argsStr = jsonObject.getString("args"); | |||||
String event = jsonObject.getString("event"); | |||||
return new ContractEventSendOpTemplate(Bytes.fromBase58(contractAddress), event, BytesUtils.toBytes(argsStr)); | |||||
} | |||||
private static BlockchainIdentityData blockchainIdentity(JSONObject jsonObject) { | |||||
JSONObject addressObj = jsonObject.getJSONObject("address"); | |||||
// base58值 | |||||
String addressBase58 = addressObj.getString("value"); | |||||
Bytes address = Bytes.fromBase58(addressBase58); | |||||
JSONObject pubKeyObj = jsonObject.getJSONObject("pubKey"); | |||||
// base58值 | |||||
String pubKeyBase58 = pubKeyObj.getString("value"); | |||||
PubKey pubKey = new PubKey(Bytes.fromBase58(pubKeyBase58).toBytes()); | |||||
// 生成对应的对象 | |||||
return new BlockchainIdentityData(address, pubKey); | |||||
} | |||||
public static class CryptoConfig implements CryptoSetting { | |||||
private short hashAlgorithm; | |||||
private boolean autoVerifyHash; | |||||
@Override | |||||
public CryptoProvider[] getSupportedProviders() { | |||||
return new CryptoProvider[0]; | |||||
} | |||||
@Override | |||||
public short getHashAlgorithm() { | |||||
return hashAlgorithm; | |||||
} | |||||
@Override | |||||
public boolean getAutoVerifyHash() { | |||||
return autoVerifyHash; | |||||
} | |||||
public void setHashAlgorithm(short hashAlgorithm) { | |||||
this.hashAlgorithm = hashAlgorithm; | |||||
} | |||||
public void setAutoVerifyHash(boolean autoVerifyHash) { | |||||
this.autoVerifyHash = autoVerifyHash; | |||||
} | |||||
} | |||||
public static class ParticipantCertData implements ParticipantNode{ | |||||
private int id; | |||||
private String address; | |||||
private String name; | |||||
private PubKey pubKey; | |||||
public ParticipantCertData() { | |||||
} | |||||
public ParticipantCertData(ParticipantNode participantNode) { | |||||
this.address = participantNode.getAddress(); | |||||
this.name = participantNode.getName(); | |||||
this.pubKey = participantNode.getPubKey(); | |||||
} | |||||
public ParticipantCertData(int id, String address, String name, PubKey pubKey) { | |||||
this.id = id; | |||||
this.address = address; | |||||
this.name = name; | |||||
this.pubKey = pubKey; | |||||
} | |||||
@Override | |||||
public String getAddress() { | |||||
return address; | |||||
} | |||||
@Override | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
@Override | |||||
public PubKey getPubKey() { | |||||
return pubKey; | |||||
} | |||||
public int getId() { | |||||
return id; | |||||
} | |||||
public void setId(int id) { | |||||
this.id = id; | |||||
} | |||||
} | |||||
} |
@@ -56,6 +56,14 @@ public class GatewayServiceFactory implements BlockchainServiceFactory, Closeabl | |||||
DataContractRegistry.register(ClientIdentifications.class); | DataContractRegistry.register(ClientIdentifications.class); | ||||
DataContractRegistry.register(ClientIdentification.class); | DataContractRegistry.register(ClientIdentification.class); | ||||
DataContractRegistry.register(CONTRACT_INT8.class); | |||||
DataContractRegistry.register(CONTRACT_INT16.class); | |||||
DataContractRegistry.register(CONTRACT_INT32.class); | |||||
DataContractRegistry.register(CONTRACT_INT64.class); | |||||
DataContractRegistry.register(CONTRACT_TEXT.class); | |||||
DataContractRegistry.register(CONTRACT_BINARY.class); | |||||
DataContractRegistry.register(ContractBizContent.class); | |||||
ByteArrayObjectUtil.init(); | ByteArrayObjectUtil.init(); | ||||
} | } | ||||
@@ -0,0 +1,93 @@ | |||||
package com.jd.blockchain.contract; | |||||
import com.jd.blockchain.utils.BaseConstant; | |||||
import com.jd.blockchain.utils.ConsoleUtils; | |||||
import com.jd.blockchain.utils.io.FileUtils; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.core.io.ClassPathResource; | |||||
import java.io.*; | |||||
import java.util.*; | |||||
import static com.jd.blockchain.utils.BaseConstant.SYS_CONTRACT_CONF; | |||||
import static com.jd.blockchain.utils.BaseConstant.SYS_CONTRACT_PROPS_NAME; | |||||
/** | |||||
* | |||||
* @author zhaogw | |||||
* date 2019/3/15 18:22 | |||||
*/ | |||||
public enum ContractConfigure { | |||||
instance(); | |||||
private static final Logger LOGGER = LoggerFactory.getLogger(ContractConfigure.class); | |||||
static Properties pp; | |||||
ContractConfigure(){ | |||||
init(); | |||||
} | |||||
private void init(){ | |||||
String contractConfPath = System.getProperty(SYS_CONTRACT_CONF); | |||||
System.out.println("contractConfPath="+contractConfPath); | |||||
try { | |||||
if (contractConfPath == null) { | |||||
ConsoleUtils.info("Load build-in default contractConf in ContractConfigure ..."); | |||||
ClassPathResource contractConfigResource = new ClassPathResource(SYS_CONTRACT_PROPS_NAME); | |||||
InputStream in = contractConfigResource.getInputStream(); | |||||
pp = FileUtils.readProperties(in, BaseConstant.CHARSET_UTF_8); | |||||
} else { | |||||
ConsoleUtils.info("Load configuration in ContractConfigure,contractConfPath="+contractConfPath); | |||||
File file = new File(contractConfPath); | |||||
pp = FileUtils.readProperties(file, BaseConstant.CHARSET_UTF_8); | |||||
} | |||||
} catch (Exception e) { | |||||
LOGGER.info(SYS_CONTRACT_PROPS_NAME+"文件异常!"+e.getMessage()); | |||||
} | |||||
} | |||||
public String values(String key) { | |||||
if(pp == null){ | |||||
init(); | |||||
} | |||||
return pp.getProperty(key); | |||||
} | |||||
public String allValues() { | |||||
if(pp == null){ | |||||
init(); | |||||
} | |||||
Set<String> allKeys = pp.stringPropertyNames(); | |||||
List<String> propList = new ArrayList(); | |||||
for(String _key : allKeys){ | |||||
String value = pp.getProperty(_key); | |||||
propList.add(_key+": "+value); | |||||
LOGGER.info("key={}, value={}",_key,value); | |||||
} | |||||
return propList.toString(); | |||||
} | |||||
//写入资源文件信息 | |||||
public static void writeProperties(String fileAllName, String comments, Map<String,String> map){ | |||||
Properties properties=new Properties(); | |||||
try { | |||||
File file = new File(fileAllName); | |||||
if (!file.getParentFile().exists()) { | |||||
boolean result = file.getParentFile().mkdirs(); | |||||
if (!result) { | |||||
System.out.println("文件创建失败."); | |||||
} | |||||
} | |||||
OutputStream outputStream=new FileOutputStream(file); | |||||
for(Map.Entry<String,String> entry : map.entrySet()){ | |||||
properties.setProperty(entry.getKey(), entry.getValue()); | |||||
} | |||||
properties.store(outputStream, comments); | |||||
outputStream.close(); | |||||
} catch (FileNotFoundException e) { | |||||
e.printStackTrace(); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,53 @@ | |||||
package com.jd.blockchain.contract.samples; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
import com.jd.blockchain.contract.Contract; | |||||
import com.jd.blockchain.contract.ContractEvent; | |||||
import com.jd.blockchain.ledger.ContractBizContent; | |||||
import com.jd.blockchain.ledger.TransactionContentBody; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
import java.math.BigDecimal; | |||||
/** | |||||
* 示例:一个“资产管理”智能合约; | |||||
* | |||||
* @author zhaogw | |||||
*/ | |||||
@Contract | |||||
public interface AssetContract2 { | |||||
/** | |||||
* 发行资产; | |||||
* 新发行的资产数量; | |||||
* @param assetHolderAddress | |||||
* 新发行的资产的持有账户; | |||||
*/ | |||||
@ContractEvent(name = "issue-asset-0") | |||||
void issue(ContractBizContent contractBizContent, String assetHolderAddress); | |||||
/** | |||||
* issue asset; | |||||
* @param contractBizContent | |||||
* @param assetHolderAddress | |||||
* @param cashNumber | |||||
*/ | |||||
@ContractEvent(name = "issue-asset") | |||||
public void issue(ContractBizContent contractBizContent, String assetHolderAddress, long cashNumber); | |||||
/** | |||||
* Bytes can bring the byte[]; | |||||
* @param bytes | |||||
* @param assetHolderAddress | |||||
* @param cashNumber | |||||
*/ | |||||
@ContractEvent(name = "issue-asset-2") | |||||
void issue(Bytes bytes,String assetHolderAddress, long cashNumber); | |||||
@ContractEvent(name = "issue-asset-3") | |||||
void issue(Byte bytes, String assetHolderAddress, long cashNumber); | |||||
@ContractEvent(name = "issue-asset-4") | |||||
void issue(Byte byteObj, String assetHolderAddress, Bytes cashNumber); | |||||
} |
@@ -25,20 +25,24 @@ public class SDKDemo_Contract { | |||||
public static BlockchainKeypair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate("ED25519"); | public static BlockchainKeypair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate("ED25519"); | ||||
public static void main(String[] args) { | |||||
demoContract(); | |||||
} | |||||
/** | /** | ||||
* 演示合约执行的过程; | * 演示合约执行的过程; | ||||
*/ | */ | ||||
public static void demoContract() { | public static void demoContract() { | ||||
// 账本地址; | // 账本地址; | ||||
String ledgerAddress = "ffkjhkeqwiuhivnsh3298josijdocaijsda=="; | |||||
String ledgerAddress = "j5rpuGWVxSuUbU3gK7MDREfui797AjfdHzvAMiSaSzydu7"; | |||||
// 节点地址列表; | // 节点地址列表; | ||||
NetworkAddress[] peerAddrs = { new NetworkAddress("192.168.10.10", 8080), | |||||
new NetworkAddress("192.168.10.11", 8080), new NetworkAddress("192.168.10.12", 8080), | |||||
new NetworkAddress("192.168.10.13", 8080) }; | |||||
// NetworkAddress[] peerAddrs = { new NetworkAddress("192.168.10.10", 8080), | |||||
// new NetworkAddress("192.168.10.11", 8080), new NetworkAddress("192.168.10.12", 8080), | |||||
// new NetworkAddress("192.168.10.13", 8080) }; | |||||
// 创建服务代理; | // 创建服务代理; | ||||
final String GATEWAY_IP = "127.0.0.1"; | |||||
final int GATEWAY_PORT = 80; | |||||
final String GATEWAY_IP = "localhost"; | |||||
final int GATEWAY_PORT = 11000; | |||||
final boolean SECURE = false; | final boolean SECURE = false; | ||||
GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IP, GATEWAY_PORT, SECURE, | GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IP, GATEWAY_PORT, SECURE, | ||||
CLIENT_CERT); | CLIENT_CERT); | ||||
@@ -52,13 +56,13 @@ public class SDKDemo_Contract { | |||||
// 一个贸易账户,贸易结算后的利润将通过一个合约账户来执行利润分配; | // 一个贸易账户,贸易结算后的利润将通过一个合约账户来执行利润分配; | ||||
// 合约账户被设置为通用的账户,不具备对贸易结算账户的直接权限; | // 合约账户被设置为通用的账户,不具备对贸易结算账户的直接权限; | ||||
// 只有当前交易发起人具备对贸易账户的直接权限,当交易发起人对交易进行签名之后,权限被间接传递给合约账户; | // 只有当前交易发起人具备对贸易账户的直接权限,当交易发起人对交易进行签名之后,权限被间接传递给合约账户; | ||||
String commerceAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||||
String commerceAccount = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; | |||||
// 处理利润分成的通用业务逻辑的合约账户; | // 处理利润分成的通用业务逻辑的合约账户; | ||||
String profitDistributionContract = "AAdfe4346fHhefe34fwf343kaeER4678RT=="; | |||||
String profitDistributionContract = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; | |||||
// 收益人账户; | // 收益人账户; | ||||
String receiptorAccount1 = "MMMEy902jkjjJJDkshreGeasdfassdfajjf=="; | |||||
String receiptorAccount2 = "Kjfe8832hfa9jjjJJDkshrFjksjdlkfj93F=="; | |||||
String receiptorAccount1 = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; | |||||
String receiptorAccount2 = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; | |||||
// 资产编码; | // 资产编码; | ||||
String assetKey = "RMB-ASSET"; | String assetKey = "RMB-ASSET"; | ||||
// 此次待分配利润; | // 此次待分配利润; | ||||
@@ -68,9 +72,12 @@ public class SDKDemo_Contract { | |||||
Remark remark = new Remark(); | Remark remark = new Remark(); | ||||
String remarkJSON = JSONSerializeUtils.serializeToJSON(remark); | String remarkJSON = JSONSerializeUtils.serializeToJSON(remark); | ||||
// AssetContract assetContract = txTemp.contract("", AssetContract.class); | |||||
// txTemp.contractInvocation(assetContract.issue(amount, assetHolderAddress)) | |||||
AssetContract assetContract = txTemp.contract(profitDistributionContract, AssetContract.class); | |||||
assetContract.issue(1000, receiptorAccount1); | |||||
assetContract.transfer(receiptorAccount1, receiptorAccount2, 600); | |||||
// assetContract. | |||||
// -------------------------------------- | // -------------------------------------- | ||||
// TX 准备就绪; | // TX 准备就绪; | ||||
@@ -0,0 +1,354 @@ | |||||
package test.com.jd.blockchain.sdk.test; | |||||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
import com.jd.blockchain.contract.samples.AssetContract; | |||||
import com.jd.blockchain.contract.samples.AssetContract2; | |||||
import com.jd.blockchain.crypto.*; | |||||
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_Contract; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.codec.Base58Utils; | |||||
import com.jd.blockchain.utils.io.ByteArray; | |||||
import com.jd.blockchain.utils.net.NetworkAddress; | |||||
import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.core.io.ClassPathResource; | |||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.io.IOException; | |||||
import static org.junit.Assert.*; | |||||
/** | |||||
* 演示合约执行的过程; | |||||
* | |||||
* @author zhaogw | |||||
* 2019-05-21 11:03 | |||||
*/ | |||||
public class SDK_Contract_Test { | |||||
public static Logger log = LoggerFactory.getLogger(SDKDemo_Contract.class); | |||||
public static BlockchainKeypair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate("ED25519"); | |||||
// 账本地址; | |||||
public static String ledgerAddress; | |||||
private PrivKey privKey; | |||||
private PubKey pubKey; | |||||
BlockchainService bcsrv; | |||||
AsymmetricKeypair signKeyPair; | |||||
HashDigest ledgerHash; | |||||
@Before | |||||
public void init(){ | |||||
ledgerAddress = "j5qHcS8jG6XwpE5wXv9HYMeGTb5Fs2gQao3TPQ3irqFpQL"; | |||||
ledgerHash = getLedgerHash(); | |||||
pubKey = SDK_GateWay_KeyPair_Para.pubKey0; | |||||
privKey = SDK_GateWay_KeyPair_Para.privkey0; | |||||
// 使用私钥进行签名; | |||||
signKeyPair = new BlockchainKeypair(pubKey, privKey); | |||||
// 创建服务代理; | |||||
final String GATEWAY_IP = "localhost"; | |||||
final int GATEWAY_PORT = 11000; | |||||
NetworkAddress addr = new NetworkAddress(GATEWAY_IP,GATEWAY_PORT); | |||||
GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(addr); | |||||
bcsrv = serviceFactory.getBlockchainService(); | |||||
} | |||||
/** | |||||
* 演示合约执行的过程; | |||||
*/ | |||||
// @Test | |||||
public void demoContract1() { | |||||
String dataAddress = registerData4Contract(); | |||||
// 发起交易; | |||||
TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); | |||||
String contractAddress = "LdeNg8JHFCKABJt6AaRNVCZPgY4ofGPd8MgcR"; | |||||
AssetContract2 assetContract = txTemp.contract(contractAddress, AssetContract2.class); | |||||
// assetContract.issue(transactionContentBody,contractAddress); | |||||
// assetContract.issue(transactionContentBody,contractAddress,888888); | |||||
// assetContract.issue(Bytes.fromString("zhaogw, contract based interface is OK!"),contractAddress,77777); | |||||
// assetContract.issue(Bytes.fromString("zhaogw, contract based interface is OK!"),contractAddress,77777); | |||||
Byte byteObj = Byte.parseByte("127"); | |||||
assetContract.issue(byteObj,dataAddress,321123); | |||||
// assetContract.issue(contractBizContent,dataAddress); | |||||
assetContract.issue(Byte.parseByte("126"),dataAddress,Bytes.fromString("100.234")); | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
prepTx.sign(signKeyPair); | |||||
// 提交交易; | |||||
TransactionResponse transactionResponse = prepTx.commit(); | |||||
//check; | |||||
KVDataEntry[] dataEntries = bcsrv.getDataEntries(ledgerHash,dataAddress,"total"); | |||||
assertEquals("100",dataEntries[0].getValue().toString()); | |||||
} | |||||
/** | |||||
* 演示合约执行的过程; | |||||
*/ | |||||
// @Test | |||||
public void demoContract2() throws IOException { | |||||
String contractAddress = deploy(); | |||||
String dataAddress = registerData4Contract(); | |||||
System.out.println("dataAddress="+dataAddress); | |||||
// 发起交易; | |||||
TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); | |||||
AssetContract2 assetContract = txTemp.contract(contractAddress, AssetContract2.class); | |||||
ContractBizContent contractBizContent = () -> new String[]{"param1","param2"}; | |||||
assetContract.issue(contractBizContent,dataAddress,123456); | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
prepTx.sign(signKeyPair); | |||||
// 提交交易; | |||||
TransactionResponse transactionResponse = prepTx.commit(); | |||||
//check; | |||||
assertTrue(transactionResponse.isSuccess()); | |||||
KVDataEntry[] dataEntries = bcsrv.getDataEntries(ledgerHash,dataAddress,contractBizContent.getAttrs()[0],contractBizContent.getAttrs()[1]); | |||||
assertEquals("value1",dataEntries[0].getValue().toString()); | |||||
assertEquals(888,dataEntries[1].getValue()); | |||||
} | |||||
// @Test | |||||
public void registerData(){ | |||||
// 在本地定义 TX 模板 | |||||
TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); | |||||
BlockchainKeypair dataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||||
txTemp.dataAccounts().register(dataAccount.getIdentity()); | |||||
String key1 = "jd_key1"; | |||||
String val1 = "www.jd1.com"; | |||||
String key2 = "jd_key2"; | |||||
String val2 = "www.jd2.com"; | |||||
// 定义交易,传输最简单的数字、字符串、提取合约中的地址; | |||||
txTemp.dataAccount(dataAccount.getAddress()).set(key1, val1, -1); | |||||
txTemp.dataAccount(dataAccount.getAddress()).set(key2, val2, -1); | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
prepTx.sign(signKeyPair); | |||||
// 提交交易; | |||||
TransactionResponse transactionResponse = prepTx.commit(); | |||||
assertTrue(transactionResponse.isSuccess()); | |||||
//repeat; | |||||
String[] keys = {key1,key2}; | |||||
String[] values = {"www.jd1.com.v1","www.jd2.com.v1"}; | |||||
this.setDataInDataAddress(dataAccount.getAddress(),keys,values,0); | |||||
String[] values2 = {"www.jd1.com.v2","www.jd2.com.v2"}; | |||||
this.setDataInDataAddress(dataAccount.getAddress(),keys,values2,1); | |||||
} | |||||
private String registerData4Contract(){ | |||||
// 在本地定义 TX 模板 | |||||
TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); | |||||
BlockchainKeypair dataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||||
txTemp.dataAccounts().register(dataAccount.getIdentity()); | |||||
txTemp.dataAccount(dataAccount.getAddress()).set("total", 200, -1); | |||||
txTemp.dataAccount(dataAccount.getAddress()).set("param1", "v", -1); | |||||
txTemp.dataAccount(dataAccount.getAddress()).set("param2", 123, -1); | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
prepTx.sign(signKeyPair); | |||||
// 提交交易; | |||||
TransactionResponse transactionResponse = prepTx.commit(); | |||||
assertTrue(transactionResponse.isSuccess()); | |||||
return dataAccount.getAddress().toBase58(); | |||||
} | |||||
private void setDataInDataAddress(Bytes dataAddress, String[] keys, String[] values, long version){ | |||||
// 在本地定义 TX 模板 | |||||
TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); | |||||
BlockchainKeypair dataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||||
for(int i=0; i<keys.length; i++){ | |||||
txTemp.dataAccount(dataAddress).set(keys[i], values[i], version); | |||||
} | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
prepTx.sign(signKeyPair); | |||||
// 提交交易; | |||||
TransactionResponse transactionResponse = prepTx.commit(); | |||||
assertTrue(transactionResponse.isSuccess()); | |||||
} | |||||
private String deploy() throws IOException { | |||||
ClassPathResource classPathResource = new ClassPathResource("contract.jar"); | |||||
byte[] chainCode = this.getChainCode(classPathResource.getURL().getPath()); | |||||
TransactionTemplate txTpl = this.bcsrv.newTransaction(ledgerHash); | |||||
BlockchainIdentity contractIdentity = BlockchainKeyGenerator.getInstance().generate().getIdentity(); | |||||
txTpl.contracts().deploy(contractIdentity, chainCode); | |||||
PreparedTransaction ptx = txTpl.prepare(); | |||||
ptx.sign(signKeyPair); | |||||
TransactionResponse txResp = ptx.commit(); | |||||
System.out.println("contract's address=" + contractIdentity.getAddress()); | |||||
String contractAddr = contractIdentity.getAddress().toBase58(); | |||||
log.info("contractAddr="+contractAddr); | |||||
return contractAddr; | |||||
} | |||||
public byte[] getChainCode(String path) { | |||||
byte[] chainCode = null; | |||||
File file = null; | |||||
FileInputStream input = null; | |||||
try { | |||||
file = new File(path); | |||||
input = new FileInputStream(file); | |||||
chainCode = new byte[input.available()]; | |||||
input.read(chainCode); | |||||
} catch (IOException var14) { | |||||
var14.printStackTrace(); | |||||
} finally { | |||||
try { | |||||
if (input != null) { | |||||
input.close(); | |||||
} | |||||
} catch (IOException var13) { | |||||
var13.printStackTrace(); | |||||
} | |||||
} | |||||
return chainCode; | |||||
} | |||||
/** | |||||
* 演示合约执行的过程; | |||||
*/ | |||||
public static void demoContract() { | |||||
// 账本地址; | |||||
String ledgerAddress = "j5rpuGWVxSuUbU3gK7MDREfui797AjfdHzvAMiSaSzydu7"; | |||||
// 节点地址列表; | |||||
// NetworkAddress[] peerAddrs = { new NetworkAddress("192.168.10.10", 8080), | |||||
// new NetworkAddress("192.168.10.11", 8080), new NetworkAddress("192.168.10.12", 8080), | |||||
// new NetworkAddress("192.168.10.13", 8080) }; | |||||
// 创建服务代理; | |||||
final String GATEWAY_IP = "localhost"; | |||||
final int GATEWAY_PORT = 11000; | |||||
final boolean SECURE = false; | |||||
GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IP, GATEWAY_PORT, SECURE, | |||||
CLIENT_CERT); | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
HashDigest ledgerHash = getLedgerHash(); | |||||
// 发起交易; | |||||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||||
// -------------------------------------- | |||||
// 一个贸易账户,贸易结算后的利润将通过一个合约账户来执行利润分配; | |||||
// 合约账户被设置为通用的账户,不具备对贸易结算账户的直接权限; | |||||
// 只有当前交易发起人具备对贸易账户的直接权限,当交易发起人对交易进行签名之后,权限被间接传递给合约账户; | |||||
String commerceAccount = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; | |||||
// 处理利润分成的通用业务逻辑的合约账户; | |||||
String profitDistributionContract = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; | |||||
// 收益人账户; | |||||
String receiptorAccount1 = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; | |||||
String receiptorAccount2 = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; | |||||
// 资产编码; | |||||
String assetKey = "RMB-ASSET"; | |||||
// 此次待分配利润; | |||||
long profit = 1000000; | |||||
// 备注信息; | |||||
Remark remark = new Remark(); | |||||
String remarkJSON = JSONSerializeUtils.serializeToJSON(remark); | |||||
AssetContract assetContract = txTemp.contract(profitDistributionContract, AssetContract.class); | |||||
assetContract.issue(1000, receiptorAccount1); | |||||
assetContract.transfer(receiptorAccount1, receiptorAccount2, 600); | |||||
// assetContract. | |||||
// -------------------------------------- | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
String txHash = ByteArray.toBase64(prepTx.getHash().toBytes()); | |||||
// 使用私钥进行签名; | |||||
AsymmetricKeypair keyPair = getSponsorKey(); | |||||
prepTx.sign(keyPair); | |||||
// 提交交易; | |||||
prepTx.commit(); | |||||
} | |||||
private static HashDigest getLedgerHash() { | |||||
return new HashDigest(Base58Utils.decode(ledgerAddress)); | |||||
} | |||||
/** | |||||
* 交易发起人的私钥;<br> | |||||
* | |||||
* 注:私钥由调用方在本地保管和使用; | |||||
* | |||||
* @return | |||||
*/ | |||||
private static AsymmetricKeypair getSponsorKey() { | |||||
SignatureFunction signatureFunction = Crypto.getSignatureFunction("ED25519"); | |||||
return signatureFunction.generateKeypair(); | |||||
} | |||||
/** | |||||
* 商品信息; | |||||
* | |||||
* @author huanghaiquan | |||||
* | |||||
*/ | |||||
public static class Remark { | |||||
private String code; | |||||
private String name; | |||||
private String venderAddress; | |||||
public String getCode() { | |||||
return code; | |||||
} | |||||
public void setCode(String code) { | |||||
this.code = code; | |||||
} | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
public void setName(String name) { | |||||
this.name = name; | |||||
} | |||||
public String getVenderAddress() { | |||||
return venderAddress; | |||||
} | |||||
public void setVenderAddress(String venderAddress) { | |||||
this.venderAddress = venderAddress; | |||||
} | |||||
} | |||||
@Test | |||||
public void testStringArr(){ | |||||
ContractBizContent contractBizContent = () -> new String[]{"1","2","you are welcome!"}; | |||||
byte[] bizBytes = BinaryProtocol.encode(contractBizContent,ContractBizContent.class); | |||||
ContractBizContent actualObj = BinaryProtocol.decodeAs(bizBytes,ContractBizContent.class); | |||||
assertArrayEquals(contractBizContent.getAttrs(),actualObj.getAttrs()); | |||||
} | |||||
} |
@@ -10,16 +10,16 @@ import com.jd.blockchain.tools.keygen.KeyGenCommand; | |||||
public class SDK_GateWay_KeyPair_Para { | public class SDK_GateWay_KeyPair_Para { | ||||
public static final String PASSWORD = "abc"; | public static final String PASSWORD = "abc"; | ||||
public static final String[] PUB_KEYS = { "endPsK36imXrY66pru6ttZ8dZ3TynWekmdqoM1K7ZRRoRBBiYVzM", | |||||
"endPsK36jQE1uYpdVRSnwQXVYhgAMWTaMJiAqii7URiULoBDLUUN", | |||||
"endPsK36fc7FSecKAJCJdFhTejbPHMLaGcihJVQCv95czCq4tW5n", | |||||
"endPsK36m1grx8mkTMgh8XQHiiaNzajdC5hkuqP6pAuLmMbYkzd4" }; | |||||
public static final String[] PUB_KEYS = { "3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9", | |||||
"3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX", | |||||
"3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x", | |||||
"3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk" }; | |||||
public static final String[] PRIV_KEYS = { | public static final String[] PRIV_KEYS = { | ||||
"177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X", | |||||
"177gjwQwTdXthkutDKVgKwiq6wWfLWYuxhji1U2N1C5MzqLRWCLZXo3i2g4vpfcEAQUPG8H", | |||||
"177gjvLHUjxvAWsqVcGgV8eHgVNBvJZYDfpP9FLjTouR1gEJNiamYu1qjTNDh18XWyLg8or", | |||||
"177gk2VtYeGbK5TS2xWhbSZA4BsT9Xj5Fb8hqCzxzgbojVVcqaDSFFrFPsLbZBx7rszyCNy" }; | |||||
"177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x", | |||||
"177gju9p5zrNdHJVEQnEEKF4ZjDDYmAXyfG84V5RPGVc5xFfmtwnHA7j51nyNLUFffzz5UT", | |||||
"177gjtwLgmSx5v1hFb46ijh7L9kdbKUpJYqdKVf9afiEmAuLgo8Rck9yu5UuUcHknWJuWaF", | |||||
"177gk1pudweTq5zgJTh8y3ENCTwtSFsKyX7YnpuKPo7rKgCkCBXVXh5z2syaTCPEMbuWRns" }; | |||||
public static PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[0], PASSWORD); | public static PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[0], PASSWORD); | ||||
public static PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[1], PASSWORD); | public static PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[1], PASSWORD); | ||||
@@ -0,0 +1,13 @@ | |||||
#ledger info; | |||||
ownerPubPath=F:\\jdsk\\jdsk-files\\github\\jdchain-starter\\conf\\jd-com.pub | |||||
ownerPrvPath=F:\\jdsk\\jdsk-files\\github\\jdchain-starter\\conf\\jd-com.priv | |||||
ownerPassword=F:\\jdsk\\jdsk-files\\github\\jdchain-starter\\conf\\jd-com.pwd | |||||
ledgerHash=j5rpuGWVxSuUbU3gK7MDREfui797AjfdHzvAMiSaSzydu7 | |||||
host=localhost | |||||
port=11000 | |||||
#execute contract; | |||||
event = issue-asset | |||||
chainCodePath=F:\\jdsk\\jdsk-files\\github\\jdchain-starter\\contract-compile\\target\\contract.jar | |||||
contractArgs=1000##fromAddr##LdeNvLeq6MB6y8CoyawYxgCCYCJkrp5xoVGUw | |||||
#contractArgs=fromAddr##toAddr##30##LdeNvLeq6MB6y8CoyawYxgCCYCJkrp5xoVGUw | |||||
@@ -1,38 +1,13 @@ | |||||
package test.com.jd.blockchain.intgr; | package test.com.jd.blockchain.intgr; | ||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.Properties; | |||||
import java.util.Random; | |||||
import java.util.concurrent.CountDownLatch; | |||||
import org.springframework.core.io.ClassPathResource; | |||||
import com.jd.blockchain.consensus.ConsensusProvider; | import com.jd.blockchain.consensus.ConsensusProvider; | ||||
import com.jd.blockchain.consensus.ConsensusProviders; | import com.jd.blockchain.consensus.ConsensusProviders; | ||||
import com.jd.blockchain.consensus.ConsensusSettings; | import com.jd.blockchain.consensus.ConsensusSettings; | ||||
import com.jd.blockchain.crypto.AddressEncoding; | |||||
import com.jd.blockchain.crypto.AsymmetricKeypair; | import com.jd.blockchain.crypto.AsymmetricKeypair; | ||||
import com.jd.blockchain.crypto.Crypto; | |||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.crypto.PrivKey; | import com.jd.blockchain.crypto.PrivKey; | ||||
import com.jd.blockchain.crypto.PubKey; | |||||
import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; | import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; | ||||
import com.jd.blockchain.ledger.AccountHeader; | |||||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||||
import com.jd.blockchain.ledger.DataAccountKVSetOperation; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | |||||
import com.jd.blockchain.ledger.LedgerInfo; | |||||
import com.jd.blockchain.ledger.ParticipantNode; | |||||
import com.jd.blockchain.ledger.PreparedTransaction; | |||||
import com.jd.blockchain.ledger.TransactionResponse; | |||||
import com.jd.blockchain.ledger.TransactionTemplate; | |||||
import com.jd.blockchain.ledger.UserInfo; | |||||
import com.jd.blockchain.ledger.core.DataAccountSet; | |||||
import com.jd.blockchain.ledger.*; | |||||
import com.jd.blockchain.ledger.core.LedgerManage; | import com.jd.blockchain.ledger.core.LedgerManage; | ||||
import com.jd.blockchain.ledger.core.LedgerRepository; | import com.jd.blockchain.ledger.core.LedgerRepository; | ||||
import com.jd.blockchain.ledger.core.impl.LedgerManager; | import com.jd.blockchain.ledger.core.impl.LedgerManager; | ||||
@@ -48,10 +23,18 @@ import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.codec.HexUtils; | import com.jd.blockchain.utils.codec.HexUtils; | ||||
import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; | import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; | ||||
import com.jd.blockchain.utils.net.NetworkAddress; | import com.jd.blockchain.utils.net.NetworkAddress; | ||||
import org.springframework.core.io.ClassPathResource; | |||||
import test.com.jd.blockchain.intgr.IntegratedContext.Node; | import test.com.jd.blockchain.intgr.IntegratedContext.Node; | ||||
import test.com.jd.blockchain.intgr.perf.LedgerInitializeWebTest; | import test.com.jd.blockchain.intgr.perf.LedgerInitializeWebTest; | ||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.Properties; | |||||
import java.util.Random; | |||||
import java.util.concurrent.CountDownLatch; | |||||
public class IntegrationTest { | public class IntegrationTest { | ||||
// 合约测试使用的初始化数据; | // 合约测试使用的初始化数据; | ||||
BlockchainKeypair contractDataKey = BlockchainKeyGenerator.getInstance().generate(); | BlockchainKeypair contractDataKey = BlockchainKeyGenerator.getInstance().generate(); | ||||
@@ -547,87 +530,87 @@ public class IntegrationTest { | |||||
} | } | ||||
} | } | ||||
private LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, | |||||
BlockchainService blockchainService, IntegratedContext context) { | |||||
// valid the basic data in contract; | |||||
prepareContractData(adminKey, ledgerHash, blockchainService, context); | |||||
BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
// 定义交易; | |||||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||||
byte[] contractCode = getChainCodeBytes(); | |||||
txTpl.users().register(userKey.getIdentity()); | |||||
txTpl.contracts().deploy(contractDeployKey.getIdentity(), contractCode); | |||||
// 签名; | |||||
PreparedTransaction ptx = txTpl.prepare(); | |||||
ptx.sign(adminKey); | |||||
// 提交并等待共识返回; | |||||
TransactionResponse txResp = ptx.commit(); | |||||
// 验证结果; | |||||
txResp.getContentHash(); | |||||
Node node0 = context.getNode(0); | |||||
LedgerRepository ledgerOfNode0 = node0.getLedgerManager().getLedger(ledgerHash); | |||||
LedgerBlock block = ledgerOfNode0.getBlock(txResp.getBlockHeight()); | |||||
byte[] contractCodeInDb = ledgerOfNode0.getContractAccountSet(block).getContract(contractDeployKey.getAddress()) | |||||
.getChainCode(); | |||||
txContentHash = ptx.getHash(); | |||||
// execute the contract; | |||||
testContractExe(adminKey, ledgerHash, userKey, blockchainService, context); | |||||
return block; | |||||
} | |||||
private void testContractExe(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair userKey, | |||||
BlockchainService blockchainService, IntegratedContext context) { | |||||
LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); | |||||
LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); | |||||
// 定义交易; | |||||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||||
txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, | |||||
("888##abc##" + contractDataKey.getAddress() + "##" + previousBlock.getHash().toBase58() + "##" | |||||
+ userKey.getAddress() + "##" + contractDeployKey.getAddress() + "##" + txContentHash.toBase58() | |||||
+ "##SOME-VALUE").getBytes()); | |||||
// 签名; | |||||
PreparedTransaction ptx = txTpl.prepare(); | |||||
ptx.sign(adminKey); | |||||
// 提交并等待共识返回; | |||||
TransactionResponse txResp = ptx.commit(); | |||||
// 验证结果; | |||||
txResp.getContentHash(); | |||||
LedgerInfo latestLedgerInfo = blockchainService.getLedger(ledgerHash); | |||||
Node node0 = context.getNode(0); | |||||
LedgerRepository ledgerOfNode0 = node0.getLedgerManager().getLedger(ledgerHash); | |||||
LedgerBlock backgroundLedgerBlock = ledgerOfNode0.retrieveLatestBlock(); | |||||
// 验证合约中的赋值,外部可以获得; | |||||
DataAccountSet dataAccountSet = ledgerOfNode0.getDataAccountSet(backgroundLedgerBlock); | |||||
AsymmetricKeypair key = Crypto.getSignatureFunction("ED25519").generateKeypair(); | |||||
PubKey pubKey = key.getPubKey(); | |||||
Bytes dataAddress = AddressEncoding.generateAddress(pubKey); | |||||
// 验证userAccount,从合约内部赋值,然后外部验证;由于目前不允许输入重复的key,所以在内部合约中构建的key,不便于在外展示,屏蔽之; | |||||
// UserAccountSet userAccountSet = | |||||
// ledgerOfNode0.getUserAccountSet(backgroundLedgerBlock); | |||||
// PubKey userPubKey = new PubKey(CryptoAlgorithm.ED25519, | |||||
// userPubKeyVal.getBytes()); | |||||
// String userAddress = AddressEncoding.generateAddress(userPubKey); | |||||
// assertEquals(userAddress, userAccountSet.getUser(userAddress).getAddress()); | |||||
} | |||||
// private LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, | |||||
// BlockchainService blockchainService, IntegratedContext context) { | |||||
// // valid the basic data in contract; | |||||
// prepareContractData(adminKey, ledgerHash, blockchainService, context); | |||||
// | |||||
// BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
// | |||||
// // 定义交易; | |||||
// TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||||
// byte[] contractCode = getChainCodeBytes(); | |||||
// | |||||
// txTpl.users().register(userKey.getIdentity()); | |||||
// | |||||
// txTpl.contracts().deploy(contractDeployKey.getIdentity(), contractCode); | |||||
// | |||||
// // 签名; | |||||
// PreparedTransaction ptx = txTpl.prepare(); | |||||
// ptx.sign(adminKey); | |||||
// | |||||
// // 提交并等待共识返回; | |||||
// TransactionResponse txResp = ptx.commit(); | |||||
// | |||||
// // 验证结果; | |||||
// txResp.getContentHash(); | |||||
// | |||||
// Node node0 = context.getNode(0); | |||||
// LedgerRepository ledgerOfNode0 = node0.getLedgerManager().getLedger(ledgerHash); | |||||
// LedgerBlock block = ledgerOfNode0.getBlock(txResp.getBlockHeight()); | |||||
// byte[] contractCodeInDb = ledgerOfNode0.getContractAccountSet(block).getContract(contractDeployKey.getAddress()) | |||||
// .getChainCode(); | |||||
// txContentHash = ptx.getHash(); | |||||
// | |||||
// // execute the contract; | |||||
// testContractExe(adminKey, ledgerHash, userKey, blockchainService, context); | |||||
// | |||||
// return block; | |||||
// } | |||||
// private void testContractExe(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair userKey, | |||||
// BlockchainService blockchainService, IntegratedContext context) { | |||||
// LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); | |||||
// LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); | |||||
// | |||||
// // 定义交易; | |||||
// TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||||
// | |||||
// txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, | |||||
// ("888##abc##" + contractDataKey.getAddress() + "##" + previousBlock.getHash().toBase58() + "##" | |||||
// + userKey.getAddress() + "##" + contractDeployKey.getAddress() + "##" + txContentHash.toBase58() | |||||
// + "##SOME-VALUE").getBytes()); | |||||
// | |||||
// // 签名; | |||||
// PreparedTransaction ptx = txTpl.prepare(); | |||||
// ptx.sign(adminKey); | |||||
// | |||||
// // 提交并等待共识返回; | |||||
// TransactionResponse txResp = ptx.commit(); | |||||
// | |||||
// // 验证结果; | |||||
// txResp.getContentHash(); | |||||
// | |||||
// LedgerInfo latestLedgerInfo = blockchainService.getLedger(ledgerHash); | |||||
// | |||||
// Node node0 = context.getNode(0); | |||||
// LedgerRepository ledgerOfNode0 = node0.getLedgerManager().getLedger(ledgerHash); | |||||
// LedgerBlock backgroundLedgerBlock = ledgerOfNode0.retrieveLatestBlock(); | |||||
// | |||||
// // 验证合约中的赋值,外部可以获得; | |||||
// DataAccountSet dataAccountSet = ledgerOfNode0.getDataAccountSet(backgroundLedgerBlock); | |||||
// AsymmetricKeypair key = Crypto.getSignatureFunction("ED25519").generateKeypair(); | |||||
// PubKey pubKey = key.getPubKey(); | |||||
// Bytes dataAddress = AddressEncoding.generateAddress(pubKey); | |||||
// | |||||
// // 验证userAccount,从合约内部赋值,然后外部验证;由于目前不允许输入重复的key,所以在内部合约中构建的key,不便于在外展示,屏蔽之; | |||||
// // UserAccountSet userAccountSet = | |||||
// // ledgerOfNode0.getUserAccountSet(backgroundLedgerBlock); | |||||
// // PubKey userPubKey = new PubKey(CryptoAlgorithm.ED25519, | |||||
// // userPubKeyVal.getBytes()); | |||||
// // String userAddress = AddressEncoding.generateAddress(userPubKey); | |||||
// // assertEquals(userAddress, userAccountSet.getUser(userAddress).getAddress()); | |||||
// } | |||||
private void prepareContractData(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainService blockchainService, | private void prepareContractData(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainService blockchainService, | ||||
IntegratedContext context) { | IntegratedContext context) { | ||||
@@ -28,11 +28,14 @@ import com.jd.blockchain.utils.net.NetworkAddress; | |||||
import org.apache.commons.io.FileUtils; | import org.apache.commons.io.FileUtils; | ||||
import org.junit.Assert; | import org.junit.Assert; | ||||
import org.springframework.core.io.ClassPathResource; | import org.springframework.core.io.ClassPathResource; | ||||
import test.com.jd.blockchain.intgr.contract.AssetContract; | |||||
import test.com.jd.blockchain.intgr.contract.AssetContract2; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.math.BigDecimal; | |||||
import java.net.URL; | import java.net.URL; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
@@ -52,6 +55,7 @@ import static org.junit.Assert.*; | |||||
*/ | */ | ||||
public class IntegrationBase { | public class IntegrationBase { | ||||
public static String KEY_TOTAL = "total"; | |||||
static { | static { | ||||
DataContractRegistry.register(LedgerInitOperation.class); | DataContractRegistry.register(LedgerInitOperation.class); | ||||
@@ -105,6 +109,9 @@ public class IntegrationBase { | |||||
// 定义交易; | // 定义交易; | ||||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | ||||
txTpl.dataAccounts().register(dataAccount.getIdentity()); | txTpl.dataAccounts().register(dataAccount.getIdentity()); | ||||
txTpl.dataAccount(dataAccount.getAddress()).set("total", 200, -1); | |||||
// txTpl.dataAccount(dataAccount.getAddress()).set("param1", "v", -1); | |||||
// txTpl.dataAccount(dataAccount.getAddress()).set("param2", 200, -1); | |||||
// 签名; | // 签名; | ||||
PreparedTransaction ptx = txTpl.prepare(); | PreparedTransaction ptx = txTpl.prepare(); | ||||
@@ -430,18 +437,16 @@ public class IntegrationBase { | |||||
} | } | ||||
// 合约测试使用的初始化数据; | // 合约测试使用的初始化数据; | ||||
BlockchainKeypair contractDataKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
static BlockchainKeypair contractDataKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
static BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
// 保存资产总数的键; | // 保存资产总数的键; | ||||
private static final String KEY_TOTAL = "TOTAL"; | |||||
// 第二个参数; | // 第二个参数; | ||||
private static final String KEY_ABC = "abc"; | |||||
private String contractZipName = "Example1.jar"; | |||||
HashDigest txContentHash; | |||||
String pubKeyVal = "jd.com"+System.currentTimeMillis(); | |||||
private String eventName = "issue-asset"; | |||||
public LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, | |||||
private static String contractZipName = "contract.jar"; | |||||
static HashDigest txContentHash; | |||||
public static LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, | |||||
BlockchainService blockchainService,LedgerRepository ledgerRepository) { | BlockchainService blockchainService,LedgerRepository ledgerRepository) { | ||||
KeyPairResponse keyPairResponse = testSDK_RegisterDataAccount(adminKey,ledgerHash,blockchainService); | |||||
System.out.println("adminKey="+ AddressEncoding.generateAddress(adminKey.getPubKey())); | System.out.println("adminKey="+ AddressEncoding.generateAddress(adminKey.getPubKey())); | ||||
BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); | BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); | ||||
System.out.println("userKey="+userKey.getAddress()); | System.out.println("userKey="+userKey.getAddress()); | ||||
@@ -460,22 +465,22 @@ public class IntegrationBase { | |||||
TransactionResponse txResp = ptx.commit(); | TransactionResponse txResp = ptx.commit(); | ||||
assertTrue(txResp.isSuccess()); | assertTrue(txResp.isSuccess()); | ||||
// 验证结果; | |||||
txResp.getContentHash(); | |||||
// 验证结果hash;请求的hash=相应的内容hash; | |||||
assertEquals(ptx.getHash(),txResp.getContentHash()); | |||||
LedgerBlock block = ledgerRepository.getBlock(txResp.getBlockHeight()); | LedgerBlock block = ledgerRepository.getBlock(txResp.getBlockHeight()); | ||||
byte[] contractCodeInDb = ledgerRepository.getContractAccountSet(block).getContract(contractDeployKey.getAddress()) | byte[] contractCodeInDb = ledgerRepository.getContractAccountSet(block).getContract(contractDeployKey.getAddress()) | ||||
.getChainCode(); | .getChainCode(); | ||||
assertArrayEquals(contractCode, contractCodeInDb); | assertArrayEquals(contractCode, contractCodeInDb); | ||||
txContentHash = ptx.getHash(); | |||||
// execute the contract; | // execute the contract; | ||||
testContractExe(adminKey, ledgerHash, userKey, blockchainService, ledgerRepository); | |||||
testContractExe(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); | |||||
testContractExe1(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); | |||||
return block; | return block; | ||||
} | } | ||||
private void testContractExe(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair userKey, | |||||
private static <T> void testContractExe(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair dataKey, | |||||
BlockchainService blockchainService,LedgerRepository ledgerRepository) { | BlockchainService blockchainService,LedgerRepository ledgerRepository) { | ||||
LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); | LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); | ||||
LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); | LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); | ||||
@@ -483,8 +488,38 @@ public class IntegrationBase { | |||||
// 定义交易; | // 定义交易; | ||||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | ||||
txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, | |||||
("888##123##" + contractDataKey.getAddress()).getBytes()); | |||||
Byte byteObj = Byte.parseByte("123"); | |||||
// txTpl.contract(contractDeployKey.getAddress(),AssetContract2.class).issue(byteObj, | |||||
// contractDeployKey.getAddress().toBase58(),321123); | |||||
txTpl.contract(contractDeployKey.getAddress(),AssetContract2.class).issue(byteObj, | |||||
dataKey.getAddress().toBase58(),Bytes.fromString("123321")); | |||||
// 签名; | |||||
PreparedTransaction ptx = txTpl.prepare(); | |||||
ptx.sign(adminKey); | |||||
// 提交并等待共识返回; | |||||
TransactionResponse txResp = ptx.commit(); | |||||
// 验证结果; | |||||
Assert.assertTrue(txResp.isSuccess()); | |||||
assertEquals(ptx.getHash(),txResp.getContentHash()); | |||||
LedgerBlock block = ledgerRepository.getBlock(txResp.getBlockHeight()); | |||||
KVDataEntry[] kvDataEntries = ledgerRepository.getDataAccountSet(block).getDataAccount(dataKey.getAddress()).getDataEntries(0,1); | |||||
assertEquals("100",kvDataEntries[0].getValue().toString()); | |||||
} | |||||
private static <T> void testContractExe1(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair dataKey, | |||||
BlockchainService blockchainService,LedgerRepository ledgerRepository) { | |||||
LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); | |||||
LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); | |||||
// 定义交易; | |||||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||||
AssetContract2 assetContract = txTpl.contract(contractDeployKey.getAddress(), AssetContract2.class); | |||||
ContractBizContent contractBizContent = () -> new String[]{"param1","param2"}; | |||||
assetContract.issue(contractBizContent,dataKey.getAddress().toBase58(),123456); | |||||
// 签名; | // 签名; | ||||
PreparedTransaction ptx = txTpl.prepare(); | PreparedTransaction ptx = txTpl.prepare(); | ||||
@@ -494,8 +529,12 @@ public class IntegrationBase { | |||||
TransactionResponse txResp = ptx.commit(); | TransactionResponse txResp = ptx.commit(); | ||||
// 验证结果; | // 验证结果; | ||||
txResp.getContentHash(); | |||||
Assert.assertTrue(txResp.isSuccess()); | Assert.assertTrue(txResp.isSuccess()); | ||||
assertEquals(ptx.getHash(),txResp.getContentHash()); | |||||
LedgerBlock block = ledgerRepository.getBlock(txResp.getBlockHeight()); | |||||
KVDataEntry[] kvDataEntries = ledgerRepository.getDataAccountSet(block).getDataAccount(dataKey.getAddress()).getDataEntries(1,2); | |||||
assertEquals("value1",kvDataEntries[0].getValue().toString()); | |||||
assertEquals(888L,kvDataEntries[1].getValue()); | |||||
} | } | ||||
/** | /** | ||||
@@ -503,7 +542,7 @@ public class IntegrationBase { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
private byte[] getChainCodeBytes() { | |||||
private static byte[] getChainCodeBytes() { | |||||
// 构建合约的字节数组; | // 构建合约的字节数组; | ||||
byte[] contractCode = null; | byte[] contractCode = null; | ||||
File file = null; | File file = null; | ||||
@@ -1,34 +1,12 @@ | |||||
package test.com.jd.blockchain.intgr; | package test.com.jd.blockchain.intgr; | ||||
import static org.junit.Assert.assertEquals; | |||||
import static org.junit.Assert.assertNotNull; | |||||
import static org.junit.Assert.assertTrue; | |||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.Properties; | |||||
import java.util.concurrent.CountDownLatch; | |||||
import org.junit.Test; | |||||
import org.junit.runner.RunWith; | |||||
import org.springframework.core.io.ClassPathResource; | |||||
import com.jd.blockchain.consensus.ConsensusProvider; | import com.jd.blockchain.consensus.ConsensusProvider; | ||||
import com.jd.blockchain.consensus.ConsensusProviders; | |||||
import com.jd.blockchain.consensus.ConsensusSettings; | import com.jd.blockchain.consensus.ConsensusSettings; | ||||
import com.jd.blockchain.crypto.AsymmetricKeypair; | import com.jd.blockchain.crypto.AsymmetricKeypair; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.crypto.PrivKey; | import com.jd.blockchain.crypto.PrivKey; | ||||
import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; | import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; | ||||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | |||||
import com.jd.blockchain.ledger.LedgerInfo; | |||||
import com.jd.blockchain.ledger.PreparedTransaction; | |||||
import com.jd.blockchain.ledger.TransactionResponse; | |||||
import com.jd.blockchain.ledger.TransactionTemplate; | |||||
import com.jd.blockchain.ledger.*; | |||||
import com.jd.blockchain.ledger.core.LedgerRepository; | import com.jd.blockchain.ledger.core.LedgerRepository; | ||||
import com.jd.blockchain.sdk.BlockchainService; | import com.jd.blockchain.sdk.BlockchainService; | ||||
import com.jd.blockchain.sdk.client.GatewayServiceFactory; | import com.jd.blockchain.sdk.client.GatewayServiceFactory; | ||||
@@ -39,18 +17,29 @@ import com.jd.blockchain.tools.initializer.Prompter; | |||||
import com.jd.blockchain.tools.keygen.KeyGenCommand; | import com.jd.blockchain.tools.keygen.KeyGenCommand; | ||||
import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; | import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; | ||||
import com.jd.blockchain.utils.net.NetworkAddress; | import com.jd.blockchain.utils.net.NetworkAddress; | ||||
import org.junit.Test; | |||||
import org.springframework.core.io.ClassPathResource; | |||||
import test.com.jd.blockchain.intgr.IntegratedContext.Node; | import test.com.jd.blockchain.intgr.IntegratedContext.Node; | ||||
import test.com.jd.blockchain.intgr.contract.AssetContract; | |||||
import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; | import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; | ||||
import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest.NodeWebContext; | import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest.NodeWebContext; | ||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.Properties; | |||||
import java.util.concurrent.CountDownLatch; | |||||
import static org.junit.Assert.*; | |||||
/** | /** | ||||
* 测试合约,提交后不立即进行验证,因为此时可能还没有完成正式结块; | * 测试合约,提交后不立即进行验证,因为此时可能还没有完成正式结块; | ||||
*/ | */ | ||||
public class IntegrationTest2 { | public class IntegrationTest2 { | ||||
// 合约测试使用的初始化数据; | // 合约测试使用的初始化数据; | ||||
BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | ||||
private String contractZipName = "AssetContract3.contract"; | |||||
private String contractZipName = "contract.jar"; | |||||
private String eventName = "issue-asset"; | private String eventName = "issue-asset"; | ||||
@Test | @Test | ||||
@@ -315,7 +304,7 @@ public class IntegrationTest2 { | |||||
// 定义交易; | // 定义交易; | ||||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | ||||
txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, ("888##999##abc").getBytes()); | |||||
txTpl.contract(contractDeployKey.getAddress(), AssetContract.class).issue(10,"abc"); | |||||
// 签名; | // 签名; | ||||
PreparedTransaction ptx = txTpl.prepare(); | PreparedTransaction ptx = txTpl.prepare(); | ||||
@@ -0,0 +1,106 @@ | |||||
package test.com.jd.blockchain.intgr; | |||||
import com.jd.blockchain.crypto.AsymmetricKeypair; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.crypto.PrivKey; | |||||
import com.jd.blockchain.crypto.PubKey; | |||||
import com.jd.blockchain.gateway.GatewayConfigProperties; | |||||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||||
import com.jd.blockchain.ledger.core.LedgerRepository; | |||||
import com.jd.blockchain.sdk.BlockchainService; | |||||
import com.jd.blockchain.sdk.client.GatewayServiceFactory; | |||||
import com.jd.blockchain.storage.service.DbConnectionFactory; | |||||
import com.jd.blockchain.tools.initializer.LedgerBindingConfig; | |||||
import com.jd.blockchain.tools.keygen.KeyGenCommand; | |||||
import com.jd.blockchain.utils.concurrent.ThreadInvoker; | |||||
import org.junit.Test; | |||||
import test.com.jd.blockchain.intgr.initializer.LedgerInitializeTest; | |||||
import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4Nodes; | |||||
import java.util.concurrent.CountDownLatch; | |||||
import java.util.concurrent.ExecutorService; | |||||
import java.util.concurrent.Executors; | |||||
import static test.com.jd.blockchain.intgr.IntegrationBase.*; | |||||
public class IntegrationTest4Contract { | |||||
private static final boolean isContractDeployAndExe = true; | |||||
private static final String DB_TYPE_MEM = "mem"; | |||||
@Test | |||||
public void test4Memory() { | |||||
test(LedgerInitConsensusConfig.bftsmartProvider, DB_TYPE_MEM, LedgerInitConsensusConfig.memConnectionStrings); | |||||
} | |||||
public void test(String[] providers, String dbType, String[] dbConnections) { | |||||
final ExecutorService sendReqExecutors = Executors.newFixedThreadPool(10); | |||||
// 内存账本初始化 | |||||
HashDigest ledgerHash = initLedger(dbConnections); | |||||
// 启动Peer节点 | |||||
PeerTestRunner[] peerNodes = peerNodeStart(ledgerHash, dbType); | |||||
DbConnectionFactory dbConnectionFactory0 = peerNodes[0].getDBConnectionFactory(); | |||||
DbConnectionFactory dbConnectionFactory1 = peerNodes[1].getDBConnectionFactory(); | |||||
DbConnectionFactory dbConnectionFactory2 = peerNodes[2].getDBConnectionFactory(); | |||||
DbConnectionFactory dbConnectionFactory3 = peerNodes[3].getDBConnectionFactory(); | |||||
String encodedBase58Pwd = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeTest.PASSWORD); | |||||
GatewayConfigProperties.KeyPairConfig gwkey0 = new GatewayConfigProperties.KeyPairConfig(); | |||||
gwkey0.setPubKeyValue(IntegrationBase.PUB_KEYS[0]); | |||||
gwkey0.setPrivKeyValue(IntegrationBase.PRIV_KEYS[0]); | |||||
gwkey0.setPrivKeyPassword(encodedBase58Pwd); | |||||
GatewayTestRunner gateway = new GatewayTestRunner("127.0.0.1", 11000, gwkey0, | |||||
peerNodes[0].getServiceAddress(), providers,null); | |||||
ThreadInvoker.AsyncCallback<Object> gwStarting = gateway.start(); | |||||
gwStarting.waitReturn(); | |||||
// 执行测试用例之前,校验每个节点的一致性; | |||||
LedgerRepository[] ledgers = buildLedgers(new LedgerBindingConfig[]{ | |||||
peerNodes[0].getLedgerBindingConfig(), | |||||
peerNodes[1].getLedgerBindingConfig(), | |||||
peerNodes[2].getLedgerBindingConfig(), | |||||
peerNodes[3].getLedgerBindingConfig(), | |||||
}, | |||||
new DbConnectionFactory[]{ | |||||
dbConnectionFactory0, | |||||
dbConnectionFactory1, | |||||
dbConnectionFactory2, | |||||
dbConnectionFactory3}); | |||||
IntegrationBase.testConsistencyAmongNodes(ledgers); | |||||
LedgerRepository ledgerRepository = ledgers[0]; | |||||
GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(gateway.getServiceAddress()); | |||||
PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(IntegrationBase.PRIV_KEYS[0], IntegrationBase.PASSWORD); | |||||
PubKey pubKey0 = KeyGenCommand.decodePubKey(IntegrationBase.PUB_KEYS[0]); | |||||
AsymmetricKeypair adminKey = new AsymmetricKeypair(pubKey0, privkey0); | |||||
BlockchainService blockchainService = gwsrvFact.getBlockchainService(); | |||||
if(isContractDeployAndExe){ | |||||
IntegrationBase.testSDK_Contract(adminKey,ledgerHash,blockchainService,ledgerRepository); | |||||
} | |||||
try { | |||||
System.out.println("----------------- Init Completed -----------------"); | |||||
Thread.sleep(Integer.MAX_VALUE); | |||||
} catch (InterruptedException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
IntegrationBase.testConsistencyAmongNodes(ledgers); | |||||
} | |||||
private HashDigest initLedger(String[] dbConnections) { | |||||
LedgerInitializeWeb4Nodes ledgerInit = new LedgerInitializeWeb4Nodes(); | |||||
HashDigest ledgerHash = ledgerInit.testInitWith4Nodes(LedgerInitConsensusConfig.bftsmartConfig, dbConnections); | |||||
System.out.printf("LedgerHash = %s \r\n", ledgerHash.toBase58()); | |||||
return ledgerHash; | |||||
} | |||||
} |
@@ -1,36 +1,8 @@ | |||||
package test.com.jd.blockchain.intgr; | package test.com.jd.blockchain.intgr; | ||||
import static org.junit.Assert.assertArrayEquals; | |||||
import static org.junit.Assert.assertEquals; | |||||
import static org.junit.Assert.assertNotNull; | |||||
import static org.junit.Assert.assertTrue; | |||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.Random; | |||||
import org.junit.Test; | |||||
import org.springframework.core.io.ClassPathResource; | |||||
import com.jd.blockchain.crypto.AddressEncoding; | |||||
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.PubKey; | |||||
import com.jd.blockchain.crypto.*; | |||||
import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; | import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; | ||||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||||
import com.jd.blockchain.ledger.DataAccountKVSetOperation; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | |||||
import com.jd.blockchain.ledger.LedgerInfo; | |||||
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.*; | |||||
import com.jd.blockchain.ledger.core.DataAccount; | import com.jd.blockchain.ledger.core.DataAccount; | ||||
import com.jd.blockchain.ledger.core.DataAccountSet; | import com.jd.blockchain.ledger.core.DataAccountSet; | ||||
import com.jd.blockchain.ledger.core.LedgerManage; | import com.jd.blockchain.ledger.core.LedgerManage; | ||||
@@ -47,9 +19,19 @@ import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.codec.HexUtils; | import com.jd.blockchain.utils.codec.HexUtils; | ||||
import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; | import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; | ||||
import com.jd.blockchain.utils.net.NetworkAddress; | import com.jd.blockchain.utils.net.NetworkAddress; | ||||
import org.junit.Test; | |||||
import org.springframework.core.io.ClassPathResource; | |||||
import test.com.jd.blockchain.intgr.contract.AssetContract; | |||||
import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; | import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; | ||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.util.Random; | |||||
import static org.junit.Assert.*; | |||||
public class IntegrationTestAll4Redis { | public class IntegrationTestAll4Redis { | ||||
public static final String PASSWORD = "abc"; | public static final String PASSWORD = "abc"; | ||||
@@ -450,10 +432,7 @@ public class IntegrationTestAll4Redis { | |||||
// 定义交易; | // 定义交易; | ||||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | ||||
txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, | |||||
("888##abc##" + contractDataKey.getAddress() + "##" + previousBlock.getHash().toBase58() + "##" | |||||
+ userKey.getAddress() + "##" + contractDeployKey.getAddress() + "##" + txContentHash.toBase58() | |||||
+ "##" + pubKeyVal).getBytes()); | |||||
txTpl.contract(contractDeployKey.getAddress(), AssetContract.class).issue(10,"abc"); | |||||
// 签名; | // 签名; | ||||
PreparedTransaction ptx = txTpl.prepare(); | PreparedTransaction ptx = txTpl.prepare(); | ||||
@@ -0,0 +1,39 @@ | |||||
package test.com.jd.blockchain.intgr.contract; | |||||
import com.jd.blockchain.contract.Contract; | |||||
import com.jd.blockchain.contract.ContractEvent; | |||||
/** | |||||
* 示例:一个“资产管理”智能合约; | |||||
* | |||||
* @author huanghaiquan | |||||
* | |||||
*/ | |||||
@Contract | |||||
public interface AssetContract { | |||||
/** | |||||
* 发行资产; | |||||
* | |||||
* @param amount | |||||
* 新发行的资产数量; | |||||
* @param assetHolderAddress | |||||
* 新发行的资产的持有账户; | |||||
*/ | |||||
@ContractEvent(name = "issue-asset") | |||||
void issue(long amount, String assetHolderAddress); | |||||
/** | |||||
* 转移资产 | |||||
* | |||||
* @param fromAddress | |||||
* 转出账户; | |||||
* @param toAddress | |||||
* 转入账户; | |||||
* @param amount | |||||
* 转移的资产数额; | |||||
*/ | |||||
@ContractEvent(name = "transfer-asset") | |||||
void transfer(String fromAddress, String toAddress, long amount); | |||||
} |
@@ -0,0 +1,44 @@ | |||||
package test.com.jd.blockchain.intgr.contract; | |||||
import com.jd.blockchain.contract.Contract; | |||||
import com.jd.blockchain.contract.ContractEvent; | |||||
import com.jd.blockchain.ledger.ContractBizContent; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
import java.math.BigDecimal; | |||||
/** | |||||
* 示例:一个“资产管理”智能合约; | |||||
* | |||||
* @author zhaogw | |||||
*/ | |||||
@Contract | |||||
public interface AssetContract2 { | |||||
/** | |||||
* 发行资产; | |||||
* 新发行的资产数量; | |||||
* @param assetHolderAddress | |||||
* 新发行的资产的持有账户; | |||||
*/ | |||||
@ContractEvent(name = "issue-asset-0") | |||||
void issue(ContractBizContent contractBizContent, String assetHolderAddress); | |||||
/** | |||||
* 发行资产; | |||||
* 新发行的资产数量; | |||||
* @param assetHolderAddress | |||||
* 新发行的资产的持有账户; | |||||
*/ | |||||
@ContractEvent(name = "issue-asset") | |||||
void issue(ContractBizContent contractBizContent, String assetHolderAddress, long cashNumber); | |||||
@ContractEvent(name = "issue-asset-2") | |||||
void issue(Bytes bytes, String assetHolderAddress, long cashNumber); | |||||
@ContractEvent(name = "issue-asset-3") | |||||
void issue(Byte byteObj, String assetHolderAddress, long cashNumber); | |||||
@ContractEvent(name = "issue-asset-4") | |||||
void issue(Byte byteObj, String assetHolderAddress, Bytes cashNumber); | |||||
} |
@@ -1,4 +1,3 @@ | |||||
#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; | #账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; | ||||
ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe | ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe | ||||
@@ -15,6 +15,7 @@ | |||||
<module>tools-initializer</module> | <module>tools-initializer</module> | ||||
<module>tools-initializer-booter</module> | <module>tools-initializer-booter</module> | ||||
<module>tools-capability</module> | <module>tools-capability</module> | ||||
<!-- <module>tools-package</module> --> | |||||
<module>tools-mocker</module> | |||||
<!-- <module>tools-package</module> --> | |||||
</modules> | </modules> | ||||
</project> | </project> |
@@ -127,7 +127,7 @@ public class LedgerInitProperties { | |||||
return resolve(props); | return resolve(props); | ||||
} | } | ||||
private static LedgerInitProperties resolve(Properties props) { | |||||
public static LedgerInitProperties resolve(Properties props) { | |||||
String hexLedgerSeed = PropertiesUtils.getRequiredProperty(props, LEDGER_SEED).replace("-", ""); | String hexLedgerSeed = PropertiesUtils.getRequiredProperty(props, LEDGER_SEED).replace("-", ""); | ||||
byte[] ledgerSeed = HexUtils.decode(hexLedgerSeed); | byte[] ledgerSeed = HexUtils.decode(hexLedgerSeed); | ||||
LedgerInitProperties initProps = new LedgerInitProperties(ledgerSeed); | LedgerInitProperties initProps = new LedgerInitProperties(ledgerSeed); | ||||
@@ -78,7 +78,7 @@ public class LocalConfig { | |||||
return resolve(props, null); | return resolve(props, null); | ||||
} | } | ||||
private static LocalConfig resolve(Properties props, String initSettingFile) { | |||||
public static LocalConfig resolve(Properties props, String initSettingFile) { | |||||
LocalConfig conf = new LocalConfig(); | LocalConfig conf = new LocalConfig(); | ||||
@@ -0,0 +1,44 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
<parent> | |||||
<artifactId>tools</artifactId> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<version>0.9.0-SNAPSHOT</version> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>tools-mocker</artifactId> | |||||
<name>tools-mocker</name> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>peer</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>gateway</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>tools-initializer</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>sdk-client</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>crypto-classic</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
</dependencies> | |||||
</project> |
@@ -0,0 +1,342 @@ | |||||
package com.jd.blockchain.mocker; | |||||
import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
import com.jd.blockchain.consensus.ConsensusProvider; | |||||
import com.jd.blockchain.consensus.ConsensusProviders; | |||||
import com.jd.blockchain.consensus.ConsensusSettings; | |||||
import com.jd.blockchain.crypto.*; | |||||
import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | |||||
import com.jd.blockchain.crypto.service.sm.SMCryptoService; | |||||
import com.jd.blockchain.ledger.*; | |||||
import com.jd.blockchain.ledger.core.*; | |||||
import com.jd.blockchain.ledger.core.impl.LedgerManager; | |||||
import com.jd.blockchain.storage.service.DbConnection; | |||||
import com.jd.blockchain.storage.service.DbConnectionFactory; | |||||
import com.jd.blockchain.tools.initializer.*; | |||||
import com.jd.blockchain.tools.initializer.LedgerInitProperties.ConsensusParticipantConfig; | |||||
import com.jd.blockchain.tools.initializer.web.LedgerInitConsensusService; | |||||
import com.jd.blockchain.tools.initializer.web.LedgerInitDecisionData; | |||||
import com.jd.blockchain.transaction.DigitalSignatureBlob; | |||||
import com.jd.blockchain.transaction.LedgerInitSettingData; | |||||
import com.jd.blockchain.transaction.TxBuilder; | |||||
import com.jd.blockchain.transaction.TxRequestBuilder; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.concurrent.InvocationResult; | |||||
import com.jd.blockchain.utils.io.BytesUtils; | |||||
import org.springframework.web.bind.annotation.*; | |||||
import java.io.IOException; | |||||
import java.util.*; | |||||
/** | |||||
* 账本初始化控制器; | |||||
* | |||||
* @author huanghaiquan | |||||
* | |||||
*/ | |||||
public class MockerLedgerInitializer implements LedgerInitProcess, LedgerInitConsensusService { | |||||
static { | |||||
DataContractRegistry.register(TransactionRequest.class); | |||||
} | |||||
private static final String[] SUPPORTED_PROVIDERS = { | |||||
ClassicCryptoService.class.getName(), | |||||
SMCryptoService.class.getName() }; | |||||
private static final String DEFAULT_SIGN_ALGORITHM = "ED25519"; | |||||
private final SignatureFunction SIGN_FUNC; | |||||
private volatile LedgerInitPermission localPermission; | |||||
private TransactionContent initTxContent; | |||||
private volatile int currentId = -1; | |||||
private volatile LedgerInitSetting ledgerInitSetting; | |||||
// private volatile LedgerInitPermission[] permissions; | |||||
// private volatile LedgerInitPermission permission; | |||||
private volatile Prompter prompter; | |||||
private volatile ConsensusProvider consensusProvider; | |||||
private volatile LedgerBlock genesisBlock; | |||||
private volatile LedgerInitDecision localDecision; | |||||
private volatile DecisionResultHandle[] decisions; | |||||
private volatile DbConnection dbConn; | |||||
private volatile LedgerEditor ledgerEditor; | |||||
private LedgerManager ledgerManager; | |||||
private DbConnectionFactory dbConnFactory; | |||||
public MockerLedgerInitializer() { | |||||
this.SIGN_FUNC = Crypto.getSignatureFunction(DEFAULT_SIGN_ALGORITHM); | |||||
} | |||||
public MockerLedgerInitializer(DbConnectionFactory dbConnFactory, LedgerManager ledgerManager) { | |||||
this.SIGN_FUNC = Crypto.getSignatureFunction(DEFAULT_SIGN_ALGORITHM); | |||||
this.dbConnFactory = dbConnFactory; | |||||
this.ledgerManager = ledgerManager; | |||||
} | |||||
public int getId() { | |||||
return currentId; | |||||
} | |||||
public TransactionContent getInitTxContent() { | |||||
return initTxContent; | |||||
} | |||||
public LedgerInitPermission getLocalPermission() { | |||||
return localPermission; | |||||
} | |||||
public LedgerInitDecision getLocalDecision() { | |||||
return localDecision; | |||||
} | |||||
public void setPrompter(Prompter prompter) { | |||||
this.prompter = prompter; | |||||
} | |||||
private void setConsensusProvider(ConsensusProvider consensusProvider) { | |||||
this.consensusProvider = consensusProvider; | |||||
} | |||||
@Override | |||||
public HashDigest initialize(int currentId, PrivKey privKey, LedgerInitProperties ledgerInitProps, | |||||
DBConnectionConfig dbConnConfig, Prompter prompter) { | |||||
return initialize(currentId, privKey, ledgerInitProps, dbConnConfig, prompter, createDefaultCryptoSetting()); | |||||
} | |||||
@Override | |||||
public synchronized HashDigest initialize(int currentId, PrivKey privKey, LedgerInitProperties ledgerInitProps, | |||||
DBConnectionConfig dbConnConfig, Prompter prompter, CryptoSetting cryptoSetting) { | |||||
if (this.ledgerInitSetting != null) { | |||||
throw new IllegalStateException("ledger init process has already started."); | |||||
} | |||||
setPrompter(prompter); | |||||
ConsensusProvider csProvider = ConsensusProviders.getProvider(ledgerInitProps.getConsensusProvider()); | |||||
setConsensusProvider(csProvider); | |||||
prompter.info("Init settings and sign permision..."); | |||||
prepareLocalPermission(currentId, privKey, ledgerInitProps, null, cryptoSetting); | |||||
try { | |||||
// 连接数据库; | |||||
connectDb(dbConnConfig); | |||||
// 生成账本; | |||||
makeLocalDecision(privKey); | |||||
// 获取其它参与方的账本生成结果; | |||||
return consensusDecisions(); | |||||
} catch (Exception e) { | |||||
e.printStackTrace(); | |||||
throw e; | |||||
} finally { | |||||
closeDb(); | |||||
} | |||||
} | |||||
public DbConnection connectDb(DBConnectionConfig dbConnConfig) { | |||||
this.dbConn = dbConnFactory.connect(dbConnConfig.getUri(), dbConnConfig.getPassword()); | |||||
return dbConn; | |||||
} | |||||
public LedgerInitDecision makeLocalDecision(PrivKey privKey) { | |||||
// 生成账本; | |||||
this.ledgerEditor = ledgerManager.newLedger(this.ledgerInitSetting, dbConn.getStorageService()); | |||||
this.genesisBlock = initLedgerDataset(ledgerEditor); | |||||
// 生成签名决定; | |||||
this.localDecision = makeDecision(currentId, genesisBlock.getHash(), privKey); | |||||
this.decisions = new DecisionResultHandle[this.ledgerInitSetting.getConsensusParticipants().length]; | |||||
for (int i = 0; i < decisions.length; i++) { | |||||
// 参与者的 id 是依次递增的; | |||||
this.decisions[i] = new DecisionResultHandle(i); | |||||
} | |||||
// 预置当前参与方的“决定”到列表,避免向自己发起请求; | |||||
this.decisions[currentId].setValue(localDecision); | |||||
return localDecision; | |||||
} | |||||
public HashDigest consensusDecisions() { | |||||
// 执行提交提交; | |||||
ledgerEditor.commit(); | |||||
return genesisBlock.getHash(); | |||||
} | |||||
public void closeDb() { | |||||
if (dbConn != null) { | |||||
DbConnection connection = dbConn; | |||||
dbConn = null; | |||||
try { | |||||
connection.close(); | |||||
} catch (IOException e) { | |||||
prompter.error(e, "Error occurred on closing db connection! --" + e.getMessage()); | |||||
} | |||||
} | |||||
} | |||||
public CryptoSetting createDefaultCryptoSetting() { | |||||
CryptoProvider[] supportedProviders = new CryptoProvider[SUPPORTED_PROVIDERS.length]; | |||||
for (int i = 0; i < SUPPORTED_PROVIDERS.length; i++) { | |||||
supportedProviders[i] = Crypto.getProvider(SUPPORTED_PROVIDERS[i]); | |||||
} | |||||
CryptoConfig defCryptoSetting = new CryptoConfig(); | |||||
defCryptoSetting.setSupportedProviders(supportedProviders); | |||||
defCryptoSetting.setAutoVerifyHash(true); | |||||
defCryptoSetting.setHashAlgorithm(Crypto.getAlgorithm("SHA256")); | |||||
return defCryptoSetting; | |||||
} | |||||
public LedgerInitPermission prepareLocalPermission(int currentId, PrivKey privKey, LedgerInitProperties ledgerProps, | |||||
ConsensusSettings csSettings, CryptoSetting cryptoSetting) { | |||||
// 创建初始化配置; | |||||
LedgerInitSettingData initSetting = new LedgerInitSettingData(); | |||||
initSetting.setLedgerSeed(ledgerProps.getLedgerSeed()); | |||||
initSetting.setCryptoSetting(cryptoSetting); | |||||
List<ConsensusParticipantConfig> partiList = ledgerProps.getConsensusParticipants(); | |||||
ConsensusParticipantConfig[] parties = partiList.toArray(new ConsensusParticipantConfig[partiList.size()]); | |||||
ConsensusParticipantConfig[] orderedParties = sortAndVerify(parties); | |||||
initSetting.setConsensusParticipants(orderedParties); | |||||
// 创建默认的共识配置; | |||||
try { | |||||
byte[] csSettingBytes = new byte[1024]; | |||||
new Random().nextBytes(csSettingBytes); | |||||
initSetting.setConsensusProvider(consensusProvider.getName()); | |||||
initSetting.setConsensusSettings(new Bytes(csSettingBytes)); | |||||
} catch (Exception e) { | |||||
throw new LedgerInitException("Create default consensus config failed! --" + e.getMessage(), e); | |||||
} | |||||
if (currentId < 0 || currentId >= orderedParties.length) { | |||||
throw new LedgerInitException("Your id is out of bound of participant list!"); | |||||
} | |||||
this.currentId = currentId; | |||||
this.ledgerInitSetting = initSetting; | |||||
// 校验当前的公钥、私钥是否匹配; | |||||
byte[] testBytes = BytesUtils.toBytes(currentId); | |||||
SignatureDigest testSign = SIGN_FUNC.sign(privKey, testBytes); | |||||
PubKey myPubKey = orderedParties[currentId].getPubKey(); | |||||
if (!SIGN_FUNC.verify(testSign, myPubKey, testBytes)) { | |||||
throw new LedgerInitException("Your pub-key specified in the init-settings isn't match your priv-key!"); | |||||
} | |||||
// 生成初始化交易,并签署许可; | |||||
TransactionBuilder initTxBuilder = new TxBuilder(null);// 账本初始化交易的账本 hash 为 null; | |||||
initTxBuilder.ledgers().create(initSetting); | |||||
for (ParticipantNode p : initSetting.getConsensusParticipants()) { | |||||
// TODO:暂时只支持注册用户的初始化操作; | |||||
BlockchainIdentity superUserId = new BlockchainIdentityData(p.getPubKey()); | |||||
initTxBuilder.users().register(superUserId); | |||||
} | |||||
this.initTxContent = initTxBuilder.prepareContent(); | |||||
// 对初始交易签名,生成当前参与者的账本初始化许可; | |||||
SignatureDigest permissionSign = TxRequestBuilder.sign(initTxContent, privKey); | |||||
localPermission = new LedgerInitPermissionData(currentId, permissionSign); | |||||
this.currentId = currentId; | |||||
return localPermission; | |||||
} | |||||
private LedgerInitDecision makeDecision(int participantId, HashDigest ledgerHash, PrivKey privKey) { | |||||
byte[] dataBytes = getDecisionBytes(participantId, ledgerHash); | |||||
SignatureFunction signFunc = Crypto.getSignatureFunction(privKey.getAlgorithm()); | |||||
SignatureDigest signature = signFunc.sign(privKey, dataBytes); | |||||
LedgerInitDecisionData decision = new LedgerInitDecisionData(); | |||||
decision.setParticipantId(participantId); | |||||
decision.setLedgerHash(ledgerHash); | |||||
decision.setSignature(signature); | |||||
return decision; | |||||
} | |||||
private LedgerBlock initLedgerDataset(LedgerEditor ledgerEditor) { | |||||
// 初始化时,自动将参与方注册为账本的用户; | |||||
TxRequestBuilder txReqBuilder = new TxRequestBuilder(this.initTxContent); | |||||
// ParticipantNode[] parties = this.ledgerInitSetting.getConsensusParticipants(); | |||||
ParticipantNode parti = this.ledgerInitSetting.getConsensusParticipants()[currentId]; | |||||
PubKey pubKey = parti.getPubKey(); | |||||
SignatureDigest signDigest = this.localPermission.getTransactionSignature(); | |||||
DigitalSignatureBlob digitalSignature = new DigitalSignatureBlob(pubKey, signDigest); | |||||
txReqBuilder.addNodeSignature(digitalSignature); | |||||
TransactionRequest txRequest = txReqBuilder.buildRequest(); | |||||
LedgerTransactionContext txCtx = ledgerEditor.newTransaction(txRequest); | |||||
Operation[] ops = txRequest.getTransactionContent().getOperations(); | |||||
// 注册用户; 注:第一个操作是 LedgerInitOperation; | |||||
// TODO:暂时只支持注册用户的初始化操作; | |||||
for (int i = 1; i < ops.length; i++) { | |||||
UserRegisterOperation userRegOP = (UserRegisterOperation) ops[i]; | |||||
txCtx.getDataSet().getUserAccountSet().register(userRegOP.getUserID().getAddress(), | |||||
userRegOP.getUserID().getPubKey()); | |||||
} | |||||
txCtx.commit(TransactionState.SUCCESS); | |||||
return ledgerEditor.prepare(); | |||||
} | |||||
private byte[] getDecisionBytes(int participantId, HashDigest ledgerHash) { | |||||
return BytesUtils.concat(BytesUtils.toBytes(participantId), ledgerHash.toBytes()); | |||||
} | |||||
@Override | |||||
public LedgerInitPermission requestPermission(int requesterId, SignatureDigest signature) { | |||||
return localPermission; | |||||
} | |||||
@Override | |||||
public LedgerInitDecision synchronizeDecision(@RequestBody LedgerInitDecision initDecision) { | |||||
return localDecision; | |||||
} | |||||
/** | |||||
* 对参与者列表按照 id 进行升序排列,并校验id是否从 1 开始且没有跳跃; | |||||
* | |||||
* @param parties | |||||
* @return | |||||
*/ | |||||
private ConsensusParticipantConfig[] sortAndVerify(ConsensusParticipantConfig[] parties) { | |||||
Arrays.sort(parties, (o1, o2) -> o1.getId() - o2.getId()); | |||||
for (int i = 0; i < parties.length; i++) { | |||||
if (parties[i].getId() != i) { | |||||
throw new LedgerInitException( | |||||
"The ids of participants are not match their positions in the participant-list!"); | |||||
} | |||||
} | |||||
return parties; | |||||
} | |||||
private static class DecisionResultHandle extends InvocationResult<LedgerInitDecision> { | |||||
private final int PARTICIPANT_ID; | |||||
public DecisionResultHandle(int participantId) { | |||||
this.PARTICIPANT_ID = participantId; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,461 @@ | |||||
package com.jd.blockchain.mocker; | |||||
import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
import com.jd.blockchain.consensus.ClientIdentification; | |||||
import com.jd.blockchain.consensus.ClientIdentifications; | |||||
import com.jd.blockchain.consensus.action.ActionRequest; | |||||
import com.jd.blockchain.consensus.action.ActionResponse; | |||||
import com.jd.blockchain.crypto.*; | |||||
import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; | |||||
import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | |||||
import com.jd.blockchain.crypto.service.sm.SMCryptoService; | |||||
import com.jd.blockchain.ledger.*; | |||||
import com.jd.blockchain.ledger.core.CryptoConfig; | |||||
import com.jd.blockchain.ledger.core.LedgerDataSet; | |||||
import com.jd.blockchain.ledger.core.LedgerEditor; | |||||
import com.jd.blockchain.ledger.core.LedgerRepository; | |||||
import com.jd.blockchain.ledger.core.impl.LedgerManager; | |||||
import com.jd.blockchain.ledger.core.impl.LedgerQueryService; | |||||
import com.jd.blockchain.ledger.core.impl.TransactionBatchProcessor; | |||||
import com.jd.blockchain.mocker.config.MockerConstant; | |||||
import com.jd.blockchain.mocker.config.PresetAnswerPrompter; | |||||
import com.jd.blockchain.mocker.handler.MockerContractExeHandle; | |||||
import com.jd.blockchain.mocker.handler.MockerOperationHandleRegister; | |||||
import com.jd.blockchain.mocker.proxy.ContractProxy; | |||||
import com.jd.blockchain.service.TransactionBatchResultHandle; | |||||
import com.jd.blockchain.storage.service.DbConnectionFactory; | |||||
import com.jd.blockchain.storage.service.utils.MemoryDBConnFactory; | |||||
import com.jd.blockchain.tools.initializer.DBConnectionConfig; | |||||
import com.jd.blockchain.tools.initializer.LedgerInitProperties; | |||||
import com.jd.blockchain.tools.keygen.KeyGenCommand; | |||||
import com.jd.blockchain.transaction.*; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.io.BytesUtils; | |||||
import com.jd.blockchain.web.serializes.ByteArrayObjectUtil; | |||||
import java.util.*; | |||||
import static java.lang.reflect.Proxy.newProxyInstance; | |||||
public class MockerNodeContext implements BlockchainQueryService { | |||||
private static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(), | |||||
SMCryptoService.class.getName() }; | |||||
private static final DBConnectionConfig dbConnectionConfig = new DBConnectionConfig("memory://local/0"); | |||||
private DbConnectionFactory dbConnFactory = new MemoryDBConnFactory(); | |||||
private MockerOperationHandleRegister opHandler = new MockerOperationHandleRegister(); | |||||
private MockerContractExeHandle contractExeHandle = new MockerContractExeHandle(); | |||||
private Map<String, BlockchainKeypair> participants = new HashMap<>(); | |||||
private LedgerManager ledgerManager = new LedgerManager(); | |||||
private BlockchainKeypair defaultKeypair; | |||||
private LedgerRepository ledgerRepository; | |||||
private LedgerQueryService queryService; | |||||
private HashDigest ledgerHash; | |||||
private String ledgerSeed; | |||||
static { | |||||
DataContractRegistry.register(TransactionContent.class); | |||||
DataContractRegistry.register(TransactionContentBody.class); | |||||
DataContractRegistry.register(TransactionRequest.class); | |||||
DataContractRegistry.register(NodeRequest.class); | |||||
DataContractRegistry.register(EndpointRequest.class); | |||||
DataContractRegistry.register(TransactionResponse.class); | |||||
DataContractRegistry.register(Operation.class); | |||||
DataContractRegistry.register(ContractCodeDeployOperation.class); | |||||
DataContractRegistry.register(ContractEventSendOperation.class); | |||||
DataContractRegistry.register(DataAccountRegisterOperation.class); | |||||
DataContractRegistry.register(UserRegisterOperation.class); | |||||
DataContractRegistry.register(DataAccountKVSetOperation.class); | |||||
DataContractRegistry.register(DataAccountKVSetOperation.KVWriteEntry.class); | |||||
DataContractRegistry.register(ActionRequest.class); | |||||
DataContractRegistry.register(ActionResponse.class); | |||||
DataContractRegistry.register(ClientIdentifications.class); | |||||
DataContractRegistry.register(ClientIdentification.class); | |||||
ByteArrayObjectUtil.init(); | |||||
} | |||||
public MockerNodeContext() { | |||||
} | |||||
public MockerNodeContext(String ledgerSeed) { | |||||
this.ledgerSeed = ledgerSeed; | |||||
} | |||||
public MockerNodeContext participants(String name, BlockchainKeypair partiKey) { | |||||
participants.put(name, partiKey); | |||||
return this; | |||||
} | |||||
public MockerNodeContext participants(BlockchainKeypair partiKey) { | |||||
participants.put(partiKey.getPubKey().toBase58(), partiKey); | |||||
return this; | |||||
} | |||||
public MockerNodeContext build() { | |||||
if (ledgerSeed == null || ledgerSeed.length() == 0) { | |||||
ledgerSeed = MockerConstant.DEFAULT_LEDGER_SEED; | |||||
} | |||||
if (participants.size() < 4) { | |||||
// 缺少的需要补充,使用常量中的内容进行补充 | |||||
for (int i = 0; i < MockerConstant.PUBLIC_KEYS.length; i++) { | |||||
String pubKeyString = MockerConstant.PUBLIC_KEYS[i]; | |||||
boolean isExist = false; | |||||
// 通过公钥进行判断 | |||||
for (Map.Entry<String, BlockchainKeypair> entry : participants.entrySet()) { | |||||
String existPubKey = KeyGenCommand.encodePubKey(entry.getValue().getPubKey()); | |||||
if (pubKeyString.equals(existPubKey)) { | |||||
isExist = true; | |||||
} | |||||
} | |||||
if (!isExist) { | |||||
// 加入系统中 | |||||
PrivKey privKey = KeyGenCommand.decodePrivKeyWithRawPassword(MockerConstant.PRIVATE_KEYS[i], MockerConstant.PASSWORD); | |||||
PubKey pubKey = KeyGenCommand.decodePubKey(MockerConstant.PUBLIC_KEYS[i]); | |||||
participants(new BlockchainKeypair(pubKey, privKey)); | |||||
} | |||||
if (participants.size() >= 4) { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
LedgerInitProperties ledgerInitProperties = loadInitSetting(); | |||||
MockerLedgerInitializer mockLedgerInitializer = new MockerLedgerInitializer(dbConnFactory, ledgerManager); | |||||
ledgerHash = mockLedgerInitializer.initialize(0, defaultKeypair.getPrivKey(), | |||||
ledgerInitProperties, dbConnectionConfig, new PresetAnswerPrompter("N"), cryptoConfig()); | |||||
ledgerRepository = registerLedger(ledgerHash, dbConnectionConfig); | |||||
queryService = new LedgerQueryService(ledgerManager); | |||||
contractExeHandle.initLedger(ledgerManager, ledgerHash); | |||||
opHandler.registerHandler(contractExeHandle); | |||||
return this; | |||||
} | |||||
public String registerUser(BlockchainKeypair user) { | |||||
TxBuilder txBuilder = txBuilder(); | |||||
txBuilder.users().register(user.getIdentity()); | |||||
txProcess(txRequest(txBuilder)); | |||||
return user.getAddress().toBase58(); | |||||
} | |||||
public String registerDataAccount(BlockchainKeypair dataAccount) { | |||||
TxBuilder txBuilder = txBuilder(); | |||||
txBuilder.dataAccounts().register(dataAccount.getIdentity()); | |||||
txProcess(txRequest(txBuilder)); | |||||
return dataAccount.getAddress().toBase58(); | |||||
} | |||||
public String registerDataAccount() { | |||||
return registerDataAccount(BlockchainKeyGenerator.getInstance().generate()); | |||||
} | |||||
public void writeKv(String address, String key, String value, long version) { | |||||
TxBuilder txBuilder = txBuilder(); | |||||
txBuilder.dataAccount(address).set(key, value, version); | |||||
txProcess(txRequest(txBuilder)); | |||||
} | |||||
public void writeKv(String address, String key, long value, long version) { | |||||
TxBuilder txBuilder = txBuilder(); | |||||
txBuilder.dataAccount(address).set(key, value, version); | |||||
txProcess(txRequest(txBuilder)); | |||||
} | |||||
public void writeKv(String address, String key, byte[] value, long version) { | |||||
TxBuilder txBuilder = txBuilder(); | |||||
txBuilder.dataAccount(address).set(key, value, version); | |||||
txProcess(txRequest(txBuilder)); | |||||
} | |||||
public void writeKv(String address, String key, Bytes value, long version) { | |||||
TxBuilder txBuilder = txBuilder(); | |||||
txBuilder.dataAccount(address).set(key, value, version); | |||||
txProcess(txRequest(txBuilder)); | |||||
} | |||||
public <T> T deployContract(T contract) { | |||||
// 首先发布合约 | |||||
BlockchainIdentity identity = deployContract2Ledger(contract); | |||||
// 生成代理对象 | |||||
ContractProxy<T> contractProxy = new ContractProxy<>(identity, this, | |||||
contract, contractExeHandle); | |||||
T proxy = (T) newProxyInstance(contract.getClass().getClassLoader(), | |||||
contract.getClass().getInterfaces(), contractProxy); | |||||
return proxy; | |||||
} | |||||
private BlockchainIdentity deployContract2Ledger(Object contract) { | |||||
BlockchainIdentity contractIdentity = BlockchainKeyGenerator.getInstance().generate().getIdentity(); | |||||
// 合约发布 | |||||
// 注意此处只是将其放入内存中,而不需要真正编译为字节码 | |||||
TxBuilder txBuilder = txBuilder(); | |||||
txBuilder.contracts().deploy(contractIdentity, BytesUtils.toBytes(contract.getClass().getName())); | |||||
// 执行 | |||||
txProcess(txRequest(txBuilder)); | |||||
return contractIdentity; | |||||
} | |||||
public String getLedgerSeed() { | |||||
return ledgerSeed; | |||||
} | |||||
public HashDigest getLedgerHash() { | |||||
return ledgerHash; | |||||
} | |||||
@Override | |||||
public HashDigest[] getLedgerHashs() { | |||||
return queryService.getLedgerHashs(); | |||||
} | |||||
@Override | |||||
public LedgerInfo getLedger(HashDigest ledgerHash) { | |||||
return queryService.getLedger(ledgerHash); | |||||
} | |||||
@Override | |||||
public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash) { | |||||
return queryService.getConsensusParticipants(ledgerHash); | |||||
} | |||||
@Override | |||||
public LedgerMetadata getLedgerMetadata(HashDigest ledgerHash) { | |||||
return queryService.getLedgerMetadata(ledgerHash); | |||||
} | |||||
@Override | |||||
public LedgerBlock getBlock(HashDigest ledgerHash, long height) { | |||||
return queryService.getBlock(ledgerHash, height); | |||||
} | |||||
@Override | |||||
public LedgerBlock getBlock(HashDigest ledgerHash, HashDigest blockHash) { | |||||
return queryService.getBlock(ledgerHash, blockHash); | |||||
} | |||||
@Override | |||||
public long getTransactionCount(HashDigest ledgerHash, long height) { | |||||
return queryService.getTransactionCount(ledgerHash, height); | |||||
} | |||||
@Override | |||||
public long getTransactionCount(HashDigest ledgerHash, HashDigest blockHash) { | |||||
return queryService.getTransactionCount(ledgerHash, blockHash); | |||||
} | |||||
@Override | |||||
public long getTransactionTotalCount(HashDigest ledgerHash) { | |||||
return queryService.getTransactionTotalCount(ledgerHash); | |||||
} | |||||
@Override | |||||
public long getDataAccountCount(HashDigest ledgerHash, long height) { | |||||
return queryService.getDataAccountCount(ledgerHash, height); | |||||
} | |||||
@Override | |||||
public long getDataAccountCount(HashDigest ledgerHash, HashDigest blockHash) { | |||||
return queryService.getDataAccountCount(ledgerHash, blockHash); | |||||
} | |||||
@Override | |||||
public long getDataAccountTotalCount(HashDigest ledgerHash) { | |||||
return queryService.getDataAccountTotalCount(ledgerHash); | |||||
} | |||||
@Override | |||||
public long getUserCount(HashDigest ledgerHash, long height) { | |||||
return queryService.getUserCount(ledgerHash, height); | |||||
} | |||||
@Override | |||||
public long getUserCount(HashDigest ledgerHash, HashDigest blockHash) { | |||||
return queryService.getUserCount(ledgerHash, blockHash); | |||||
} | |||||
@Override | |||||
public long getUserTotalCount(HashDigest ledgerHash) { | |||||
return queryService.getUserTotalCount(ledgerHash); | |||||
} | |||||
@Override | |||||
public long getContractCount(HashDigest ledgerHash, long height) { | |||||
return queryService.getContractCount(ledgerHash, height); | |||||
} | |||||
@Override | |||||
public long getContractCount(HashDigest ledgerHash, HashDigest blockHash) { | |||||
return queryService.getContractCount(ledgerHash, blockHash); | |||||
} | |||||
@Override | |||||
public long getContractTotalCount(HashDigest ledgerHash) { | |||||
return queryService.getContractTotalCount(ledgerHash); | |||||
} | |||||
@Override | |||||
public LedgerTransaction[] getTransactions(HashDigest ledgerHash, long height, int fromIndex, int count) { | |||||
return queryService.getTransactions(ledgerHash, height, fromIndex, count); | |||||
} | |||||
@Override | |||||
public LedgerTransaction[] getTransactions(HashDigest ledgerHash, HashDigest blockHash, int fromIndex, int count) { | |||||
return queryService.getTransactions(ledgerHash, blockHash, fromIndex, count); | |||||
} | |||||
@Override | |||||
public LedgerTransaction getTransactionByContentHash(HashDigest ledgerHash, HashDigest contentHash) { | |||||
return queryService.getTransactionByContentHash(ledgerHash, contentHash); | |||||
} | |||||
@Override | |||||
public TransactionState getTransactionStateByContentHash(HashDigest ledgerHash, HashDigest contentHash) { | |||||
return queryService.getTransactionStateByContentHash(ledgerHash, contentHash); | |||||
} | |||||
@Override | |||||
public UserInfo getUser(HashDigest ledgerHash, String address) { | |||||
return queryService.getUser(ledgerHash, address); | |||||
} | |||||
@Override | |||||
public AccountHeader getDataAccount(HashDigest ledgerHash, String address) { | |||||
return queryService.getDataAccount(ledgerHash, address); | |||||
} | |||||
@Override | |||||
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { | |||||
return queryService.getDataEntries(ledgerHash, address, keys); | |||||
} | |||||
@Override | |||||
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO) { | |||||
return queryService.getDataEntries(ledgerHash, address, kvInfoVO); | |||||
} | |||||
@Override | |||||
public long getDataEntriesTotalCount(HashDigest ledgerHash, String address) { | |||||
return queryService.getDataEntriesTotalCount(ledgerHash, address); | |||||
} | |||||
@Override | |||||
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { | |||||
return queryService.getDataEntries(ledgerHash, address, fromIndex, count); | |||||
} | |||||
@Override | |||||
public AccountHeader getContract(HashDigest ledgerHash, String address) { | |||||
return queryService.getContract(ledgerHash, address); | |||||
} | |||||
@Override | |||||
public AccountHeader[] getUsers(HashDigest ledgerHash, int fromIndex, int count) { | |||||
return queryService.getUsers(ledgerHash, fromIndex, count); | |||||
} | |||||
@Override | |||||
public AccountHeader[] getDataAccounts(HashDigest ledgerHash, int fromIndex, int count) { | |||||
return queryService.getDataAccounts(ledgerHash, fromIndex, count); | |||||
} | |||||
@Override | |||||
public AccountHeader[] getContractAccounts(HashDigest ledgerHash, int fromIndex, int count) { | |||||
return queryService.getContractAccounts(ledgerHash, fromIndex, count); | |||||
} | |||||
public TxBuilder txBuilder() { | |||||
return new TxBuilder(ledgerHash); | |||||
} | |||||
public TransactionRequest txRequest(TxBuilder txBuilder) { | |||||
TransactionRequestBuilder reqBuilder = txBuilder.prepareRequest(); | |||||
reqBuilder.signAsEndpoint(defaultKeypair); | |||||
return reqBuilder.buildRequest(); | |||||
} | |||||
public void txProcess(TransactionRequest txRequest) { | |||||
LedgerEditor newEditor = ledgerRepository.createNextBlock(); | |||||
LedgerBlock latestBlock = ledgerRepository.getLatestBlock(); | |||||
LedgerDataSet previousDataSet = ledgerRepository.getDataSet(latestBlock); | |||||
TransactionBatchProcessor txProc = new TransactionBatchProcessor(newEditor, previousDataSet, opHandler, | |||||
ledgerManager); | |||||
txProc.schedule(txRequest); | |||||
TransactionBatchResultHandle handle = txProc.prepare(); | |||||
handle.commit(); | |||||
} | |||||
private LedgerRepository registerLedger(HashDigest ledgerHash, DBConnectionConfig dbConnConf) { | |||||
return ledgerManager.register(ledgerHash, dbConnFactory.connect(dbConnConf.getUri()).getStorageService()); | |||||
} | |||||
private CryptoConfig cryptoConfig() { | |||||
CryptoProvider[] supportedProviders = new CryptoProvider[SUPPORTED_PROVIDERS.length]; | |||||
for (int i = 0; i < SUPPORTED_PROVIDERS.length; i++) { | |||||
supportedProviders[i] = Crypto.getProvider(SUPPORTED_PROVIDERS[i]); | |||||
} | |||||
CryptoConfig cryptoSetting = new CryptoConfig(); | |||||
cryptoSetting.setSupportedProviders(supportedProviders); | |||||
cryptoSetting.setAutoVerifyHash(false); | |||||
cryptoSetting.setHashAlgorithm(ClassicAlgorithm.SHA256); | |||||
return cryptoSetting; | |||||
} | |||||
private LedgerInitProperties loadInitSetting() { | |||||
Properties ledgerProp = new Properties(); | |||||
ledgerProp.put(LedgerInitProperties.LEDGER_SEED, ledgerSeed); | |||||
ledgerProp.put("ledger.name", MockerConstant.LEDGER_NAME); | |||||
ledgerProp.put(LedgerInitProperties.CONSENSUS_SERVICE_PROVIDER, "com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"); | |||||
ledgerProp.put(LedgerInitProperties.CONSENSUS_CONFIG, "classpath:bftsmart.config"); | |||||
ledgerProp.put(LedgerInitProperties.CRYPTO_SERVICE_PROVIDERS, "com.jd.blockchain.crypto.service.classic.ClassicCryptoService," + | |||||
"com.jd.blockchain.crypto.service.sm.SMCryptoService"); | |||||
ledgerProp.put(LedgerInitProperties.PART_COUNT, String.valueOf(participants.size())); | |||||
int partiIndex = 0; | |||||
for (Map.Entry<String, BlockchainKeypair> entry : participants.entrySet()) { | |||||
String name = entry.getKey(); | |||||
BlockchainKeypair keypair = entry.getValue(); | |||||
if (partiIndex == 0) { | |||||
defaultKeypair = keypair; | |||||
} | |||||
String partiPrefix = String.format(LedgerInitProperties.PART_ID_PATTERN, partiIndex) + "."; | |||||
ledgerProp.put(partiPrefix + LedgerInitProperties.PART_NAME, name); | |||||
ledgerProp.put(partiPrefix + LedgerInitProperties.PART_PUBKEY_PATH, ""); | |||||
ledgerProp.put(partiPrefix + LedgerInitProperties.PART_PUBKEY, KeyGenCommand.encodePubKey(keypair.getPubKey())); | |||||
ledgerProp.put(partiPrefix + LedgerInitProperties.PART_INITIALIZER_HOST, MockerConstant.LOCAL_ADDRESS); | |||||
ledgerProp.put(partiPrefix + LedgerInitProperties.PART_INITIALIZER_PORT, String.valueOf(MockerConstant.LEDGER_INIT_PORT_START + partiIndex * 10)); | |||||
ledgerProp.put(partiPrefix + LedgerInitProperties.PART_INITIALIZER_SECURE, String.valueOf(false)); | |||||
partiIndex++; | |||||
} | |||||
return LedgerInitProperties.resolve(ledgerProp); | |||||
} | |||||
} |
@@ -0,0 +1,22 @@ | |||||
package com.jd.blockchain.mocker.config; | |||||
import com.jd.blockchain.storage.service.impl.composite.CompositeConnectionFactory; | |||||
import com.jd.blockchain.tools.initializer.web.InitWebSecurityConfiguration; | |||||
import com.jd.blockchain.tools.initializer.web.InitWebServerConfiguration; | |||||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |||||
import org.springframework.context.annotation.Bean; | |||||
import org.springframework.context.annotation.Configuration; | |||||
import org.springframework.context.annotation.Import; | |||||
@SpringBootApplication | |||||
@Configuration | |||||
@EnableConfigurationProperties | |||||
@Import(value = { InitWebServerConfiguration.class, InitWebSecurityConfiguration.class }) | |||||
public class LedgerInitWebConfiguration { | |||||
@Bean | |||||
public CompositeConnectionFactory getCompositeConnectionFactory() { | |||||
return new CompositeConnectionFactory(); | |||||
} | |||||
} |
@@ -0,0 +1,67 @@ | |||||
package com.jd.blockchain.mocker.config; | |||||
public class MockerConstant { | |||||
public static final String DEFAULT_LEDGER_SEED = "932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffa"; | |||||
public static final int PEER_PORT_START = 12000; | |||||
public static final int LEDGER_INIT_PORT_START = 1600; | |||||
public static final int GATEWAY_PORT = 11000; | |||||
public static final String LOCAL_ADDRESS = "127.0.0.1"; | |||||
public static final String LEDGER_NAME = "JDChain"; | |||||
public static final String LEDGER_INIT_FORMATTER = "ledger%s.init"; | |||||
public static final String[] CONSENSUS_PROVIDERS = new String[] {"com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"}; | |||||
public static final String PASSWORD = "abc"; | |||||
public static final String PASSWORD_ENCODE = "DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY"; | |||||
public static final String[] PUBLIC_KEYS = { | |||||
"3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9", | |||||
"3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX", | |||||
"3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x", | |||||
"3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk", | |||||
"3snPdw7i7PXPRMp3EAjsxJkHe7aZJRLNzdW8kEHBWeQsSgcPAiHP2J", | |||||
"3snPdw7i7PmmQoPgUpUmmAUj6nakHj8wMSQmiMi1RaiZp4YU1D4AXk", | |||||
"3snPdw7i7PiJKsa94q3EcLT1y6GRJ7LeFGe799hdzRRHmf6LNodyiM", | |||||
"3snPdw7i7Pm2wJwEnKn8kK8eGTkN36C2BZRRjVTr9FPB2rqtcgTq7h" | |||||
}; | |||||
public static final String[] ADDRESS_ARRAY = { | |||||
"LdeP3fY7jJbNwL8CiL2wU21AF9unDWQjVEW5w", | |||||
"LdeNnz88dH6CA6PwkVdn3nFRibUKP3sFT2byG", | |||||
"LdeNmdpT4DiTwLUP9jRQhwdRBRiXeHno456vy", | |||||
"LdeNekdXMHqyz9Qxc2jDSBnkvvZLbty6pRDdP", | |||||
"LdeNryu2DK96tDvtLJfBz7ArWynAWPJAep38n", | |||||
"LdeNkoQpXffVF9qjsa4A7wZVgT9W2vnhpEEm5", | |||||
"LdeNzfFrsJT7Ni1L7k1EP3NuxUfK8QGAxMGpt", | |||||
"LdeNuLhR5AoyhQoVeS15haJvvGC5ByoPezrGq" | |||||
}; | |||||
public static final String[] PRIVATE_KEYS = { | |||||
"177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x", | |||||
"177gju9p5zrNdHJVEQnEEKF4ZjDDYmAXyfG84V5RPGVc5xFfmtwnHA7j51nyNLUFffzz5UT", | |||||
"177gjtwLgmSx5v1hFb46ijh7L9kdbKUpJYqdKVf9afiEmAuLgo8Rck9yu5UuUcHknWJuWaF", | |||||
"177gk1pudweTq5zgJTh8y3ENCTwtSFsKyX7YnpuKPo7rKgCkCBXVXh5z2syaTCPEMbuWRns", | |||||
"177gjwyHzfmsD4g3MVB655seYWXua2KBdQEqTf9kHgeMc6gdRZADKb6cL13L5koqMsBtkGX", | |||||
"177gk2C9V7gwPhAGgawL53W8idDpSo63jnbg8finbZkk4zermr5aqgTeKspN45fbymey8t6", | |||||
"177gjtz29TXa2E3FFBpCNr5LpU5zYxkNPAgcAJZW7tCGUgWQr4gcVv8PHmoVVPeSVVnyZV5", | |||||
"177gjzpHnqGEuSKyi3pW69WhpEPmeFPxLNVmUfXQb4DDV2EfnMgY7T4NFsyRsThjJFsau7X"}; | |||||
public static final String[] DB_MEMS = { | |||||
"memory://127.0.0.1/0", | |||||
"memory://127.0.0.1/1", | |||||
"memory://127.0.0.1/2", | |||||
"memory://127.0.0.1/3", | |||||
"memory://127.0.0.1/4", | |||||
"memory://127.0.0.1/5", | |||||
"memory://127.0.0.1/6", | |||||
"memory://127.0.0.1/7" | |||||
}; | |||||
} |
@@ -0,0 +1,32 @@ | |||||
package com.jd.blockchain.mocker.config; | |||||
import com.jd.blockchain.tools.initializer.ConsolePrompter; | |||||
import java.util.Properties; | |||||
public class PresetAnswerPrompter extends ConsolePrompter { | |||||
private Properties answers = new Properties(); | |||||
private String defaultAnswer; | |||||
public PresetAnswerPrompter(String defaultAnswer) { | |||||
this.defaultAnswer = defaultAnswer; | |||||
} | |||||
public void setAnswer(String tag, String answer) { | |||||
answers.setProperty(tag, answer); | |||||
} | |||||
public void setDefaultAnswer(String defaultAnswer) { | |||||
this.defaultAnswer = defaultAnswer; | |||||
} | |||||
@Override | |||||
public String confirm(String tag, String format, Object... args) { | |||||
System.out.print(String.format(format, args)); | |||||
String answer = answers.getProperty(tag, defaultAnswer); | |||||
System.out.println(String.format("\r\n [Mocked answer:%s]", answer)); | |||||
return answer; | |||||
} | |||||
} |
@@ -0,0 +1,17 @@ | |||||
package com.jd.blockchain.mocker.contracts; | |||||
import com.jd.blockchain.contract.Contract; | |||||
import com.jd.blockchain.contract.ContractEvent; | |||||
@Contract | |||||
public interface AccountContract { | |||||
@ContractEvent(name = "create") | |||||
void create(String address, String account, long money); | |||||
@ContractEvent(name = "transfer") | |||||
void transfer(String address, String from, String to, long money); | |||||
@ContractEvent(name = "print") | |||||
void print(String address, String from, String to); | |||||
} |
@@ -0,0 +1,81 @@ | |||||
package com.jd.blockchain.mocker.contracts; | |||||
import com.jd.blockchain.contract.ContractEventContext; | |||||
import com.jd.blockchain.contract.ContractException; | |||||
import com.jd.blockchain.contract.EventProcessingAwire; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | |||||
public class AccountContractImpl implements EventProcessingAwire, AccountContract { | |||||
private ContractEventContext eventContext; | |||||
private HashDigest ledgerHash; | |||||
@Override | |||||
public void create(String address, String account, long money) { | |||||
// 暂不处理该账户已经存在的问题 | |||||
eventContext.getLedger().dataAccount(address).set(account, money, -1); | |||||
} | |||||
@Override | |||||
public void transfer(String address, String from, String to, long money) { | |||||
// 首先分别查询from与to的结果 | |||||
KVDataEntry[] dataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, from, to); | |||||
long currentFromMoney = 0L, currentToMoney = 0L, currentFromVer = -1L, currentToVer = -1L; | |||||
if (dataEntries != null && dataEntries.length > 0) { | |||||
for (KVDataEntry dataEntry : dataEntries) { | |||||
String key = dataEntry.getKey(); | |||||
Object value = dataEntry.getValue(); | |||||
long version = dataEntry.getVersion(); | |||||
if (key.equals(from)) { | |||||
currentFromMoney = (long) value; | |||||
currentFromVer = version; | |||||
} | |||||
if (key.equals(to)) { | |||||
currentToMoney = (long) value; | |||||
currentToVer = version; | |||||
} | |||||
} | |||||
} | |||||
currentFromMoney -= money; | |||||
currentToMoney += money; | |||||
// 重新设置结果 | |||||
eventContext.getLedger().dataAccount(address).set(from, currentFromMoney, currentFromVer) | |||||
.set(to, currentToMoney, currentToVer); | |||||
} | |||||
@Override | |||||
public void print(String address, String from, String to) { | |||||
KVDataEntry[] dataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, from, to); | |||||
if (dataEntries != null && dataEntries.length > 0) { | |||||
for (KVDataEntry dataEntry : dataEntries) { | |||||
String key = dataEntry.getKey(); | |||||
Object value = dataEntry.getValue(); | |||||
long version = dataEntry.getVersion(); | |||||
System.out.printf("Key = %s Value = %s Version = %s \r\n", key, value, version); | |||||
} | |||||
} | |||||
} | |||||
@Override | |||||
public void beforeEvent(ContractEventContext eventContext) { | |||||
this.eventContext = eventContext; | |||||
this.ledgerHash = this.eventContext.getCurrentLedgerHash(); | |||||
} | |||||
@Override | |||||
public void postEvent(ContractEventContext eventContext, ContractException error) { | |||||
} | |||||
@Override | |||||
public void postEvent(ContractException error) { | |||||
} | |||||
@Override | |||||
public void postEvent() { | |||||
} | |||||
} |
@@ -0,0 +1,14 @@ | |||||
package com.jd.blockchain.mocker.contracts; | |||||
import com.jd.blockchain.contract.Contract; | |||||
import com.jd.blockchain.contract.ContractEvent; | |||||
@Contract | |||||
public interface WriteContract { | |||||
@ContractEvent(name = "print") | |||||
void print(String name); | |||||
@ContractEvent(name = "writeKv") | |||||
void writeKv(String address, String key, String value); | |||||
} |
@@ -0,0 +1,41 @@ | |||||
package com.jd.blockchain.mocker.contracts; | |||||
import com.jd.blockchain.contract.ContractEventContext; | |||||
import com.jd.blockchain.contract.ContractException; | |||||
import com.jd.blockchain.contract.EventProcessingAwire; | |||||
public class WriteContractImpl implements EventProcessingAwire, WriteContract { | |||||
private ContractEventContext eventContext; | |||||
@Override | |||||
public void print(String name) { | |||||
System.out.printf("My Name is %s \r\n", name); | |||||
System.out.printf("My Ledger Hash is %s \r\n", eventContext.getCurrentLedgerHash().toBase58()); | |||||
} | |||||
@Override | |||||
public void writeKv(String address, String key, String value) { | |||||
eventContext.getLedger().dataAccount(address).set(key, value, -1); | |||||
} | |||||
@Override | |||||
public void beforeEvent(ContractEventContext eventContext) { | |||||
this.eventContext = eventContext; | |||||
} | |||||
@Override | |||||
public void postEvent(ContractEventContext eventContext, ContractException error) { | |||||
System.out.println("----- postEvent1 -----"); | |||||
} | |||||
@Override | |||||
public void postEvent(ContractException error) { | |||||
System.out.println("----- postEvent2 -----"); | |||||
} | |||||
@Override | |||||
public void postEvent() { | |||||
System.out.println("----- postEvent3 -----"); | |||||
} | |||||
} |
@@ -0,0 +1,54 @@ | |||||
package com.jd.blockchain.mocker.data; | |||||
public class KvData { | |||||
private String dataAccount; | |||||
private String key; | |||||
private byte[] value; | |||||
private long version; | |||||
public KvData() { | |||||
} | |||||
public KvData(String dataAccount, String key, byte[] value, long version) { | |||||
this.dataAccount = dataAccount; | |||||
this.key = key; | |||||
this.value = value; | |||||
this.version = version; | |||||
} | |||||
public String getDataAccount() { | |||||
return dataAccount; | |||||
} | |||||
public String getKey() { | |||||
return key; | |||||
} | |||||
public byte[] getValue() { | |||||
return value; | |||||
} | |||||
public long getVersion() { | |||||
return version; | |||||
} | |||||
public void setDataAccount(String dataAccount) { | |||||
this.dataAccount = dataAccount; | |||||
} | |||||
public void setKey(String key) { | |||||
this.key = key; | |||||
} | |||||
public void setValue(byte[] value) { | |||||
this.value = value; | |||||
} | |||||
public void setVersion(long version) { | |||||
this.version = version; | |||||
} | |||||
} |
@@ -0,0 +1,38 @@ | |||||
package com.jd.blockchain.mocker.data; | |||||
import com.jd.blockchain.ledger.TransactionResponse; | |||||
public class ResponseData<T> { | |||||
private TransactionResponse txResponse; | |||||
private T data; | |||||
public ResponseData() { | |||||
} | |||||
public ResponseData(TransactionResponse txResponse) { | |||||
this.txResponse = txResponse; | |||||
} | |||||
public ResponseData(TransactionResponse txResponse, T data) { | |||||
this.txResponse = txResponse; | |||||
this.data = data; | |||||
} | |||||
public TransactionResponse getTxResponse() { | |||||
return txResponse; | |||||
} | |||||
public T getData() { | |||||
return data; | |||||
} | |||||
public void setTxResponse(TransactionResponse txResponse) { | |||||
this.txResponse = txResponse; | |||||
} | |||||
public void setData(T data) { | |||||
this.data = data; | |||||
} | |||||
} |
@@ -0,0 +1,136 @@ | |||||
package com.jd.blockchain.mocker.handler; | |||||
import com.jd.blockchain.contract.ContractEventContext; | |||||
import com.jd.blockchain.contract.ContractException; | |||||
import com.jd.blockchain.contract.EventProcessingAwire; | |||||
import com.jd.blockchain.contract.LedgerContext; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||||
import com.jd.blockchain.ledger.ContractEventSendOperation; | |||||
import com.jd.blockchain.ledger.Operation; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | |||||
import com.jd.blockchain.ledger.core.*; | |||||
import com.jd.blockchain.ledger.core.impl.LedgerManager; | |||||
import com.jd.blockchain.ledger.core.impl.LedgerQueryService; | |||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | |||||
import com.jd.blockchain.ledger.core.impl.handles.ContractLedgerContext; | |||||
import com.jd.blockchain.mocker.proxy.ExecutorProxy; | |||||
import java.util.Map; | |||||
import java.util.Set; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
public class MockerContractExeHandle implements OperationHandle { | |||||
private Map<HashDigest, ExecutorProxy> executorProxyMap = new ConcurrentHashMap<>(); | |||||
private LedgerManager ledgerManager; | |||||
private HashDigest ledgerHash; | |||||
@Override | |||||
public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { | |||||
ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | |||||
HashDigest txHash = requestContext.getRequest().getTransactionContent().getHash(); | |||||
ExecutorProxy executorProxy = executorProxyMap.get(txHash); | |||||
if (executorProxy != null) { | |||||
LedgerQueryService queryService = new LedgerQueryService(ledgerManager); | |||||
ContractLedgerContext ledgerContext = new ContractLedgerContext(queryService, opHandleContext); | |||||
MockerContractEventContext contractEventContext = new MockerContractEventContext( | |||||
ledgerHash, contractOP.getEvent(), requestContext.getRequest(), ledgerContext); | |||||
EventProcessingAwire eventProcessingAwire = (EventProcessingAwire) executorProxy.getInstance(); | |||||
try { | |||||
// | |||||
// Before处理过程 | |||||
eventProcessingAwire.beforeEvent(contractEventContext); | |||||
executorProxy.invoke(); | |||||
// After处理过程 | |||||
eventProcessingAwire.postEvent(); | |||||
} catch (Exception e) { | |||||
eventProcessingAwire.postEvent(new ContractException(e.getMessage())); | |||||
} finally { | |||||
removeExecutorProxy(txHash); | |||||
} | |||||
} | |||||
} | |||||
@Override | |||||
public boolean support(Class<?> operationType) { | |||||
return ContractEventSendOperation.class.isAssignableFrom(operationType); | |||||
} | |||||
public void initLedger(LedgerManager ledgerManager, HashDigest ledgerHash) { | |||||
this.ledgerManager = ledgerManager; | |||||
this.ledgerHash = ledgerHash; | |||||
} | |||||
public void registerExecutorProxy(HashDigest hashDigest, ExecutorProxy executorProxy) { | |||||
executorProxyMap.put(hashDigest, executorProxy); | |||||
} | |||||
public ExecutorProxy removeExecutorProxy(HashDigest hashDigest) { | |||||
return executorProxyMap.remove(hashDigest); | |||||
} | |||||
public static class MockerContractEventContext implements ContractEventContext { | |||||
private HashDigest ledgeHash; | |||||
private String event; | |||||
private TransactionRequest transactionRequest; | |||||
private LedgerContext ledgerContext; | |||||
public MockerContractEventContext(HashDigest ledgeHash, String event, | |||||
TransactionRequest transactionRequest, LedgerContext ledgerContext) { | |||||
this.ledgeHash = ledgeHash; | |||||
this.event = event; | |||||
this.transactionRequest = transactionRequest; | |||||
this.ledgerContext = ledgerContext; | |||||
} | |||||
@Override | |||||
public HashDigest getCurrentLedgerHash() { | |||||
return ledgeHash; | |||||
} | |||||
@Override | |||||
public TransactionRequest getTransactionRequest() { | |||||
return transactionRequest; | |||||
} | |||||
@Override | |||||
public Set<BlockchainIdentity> getTxSigners() { | |||||
return null; | |||||
} | |||||
@Override | |||||
public String getEvent() { | |||||
return event; | |||||
} | |||||
@Override | |||||
public byte[] getArgs() { | |||||
return null; | |||||
} | |||||
@Override | |||||
public LedgerContext getLedger() { | |||||
return ledgerContext; | |||||
} | |||||
@Override | |||||
public Set<BlockchainIdentity> getContracOwners() { | |||||
return null; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,189 @@ | |||||
package com.jd.blockchain.mocker.handler; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.crypto.PrivKey; | |||||
import com.jd.blockchain.gateway.GatewayConfigProperties; | |||||
import com.jd.blockchain.mocker.config.MockerConstant; | |||||
import com.jd.blockchain.mocker.config.PresetAnswerPrompter; | |||||
import com.jd.blockchain.mocker.node.GatewayNodeRunner; | |||||
import com.jd.blockchain.mocker.node.NodeWebContext; | |||||
import com.jd.blockchain.mocker.node.PeerNodeRunner; | |||||
import com.jd.blockchain.tools.initializer.DBConnectionConfig; | |||||
import com.jd.blockchain.tools.initializer.LedgerBindingConfig; | |||||
import com.jd.blockchain.tools.initializer.LedgerInitProperties; | |||||
import com.jd.blockchain.tools.initializer.Prompter; | |||||
import com.jd.blockchain.tools.keygen.KeyGenCommand; | |||||
import com.jd.blockchain.utils.concurrent.ThreadInvoker; | |||||
import com.jd.blockchain.utils.net.NetworkAddress; | |||||
import org.springframework.util.ResourceUtils; | |||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.util.*; | |||||
import java.util.concurrent.CountDownLatch; | |||||
import java.util.concurrent.ExecutorService; | |||||
import java.util.concurrent.Executors; | |||||
import static com.jd.blockchain.mocker.config.MockerConstant.*; | |||||
public class MockerNodeHandler { | |||||
private static final String LEDGER_BINDING_FORMAT = "binding.%s."; | |||||
private static final String PARTI_FORMAT = LEDGER_BINDING_FORMAT + "parti."; | |||||
private static final String BINDING_ID_FORMAT = PARTI_FORMAT + "id"; | |||||
private static final String BINDING_PK_PATH_FORMAT = PARTI_FORMAT + "pk-path"; | |||||
private static final String BINDING_PK_FORMAT = PARTI_FORMAT + "pk"; | |||||
private static final String BINDING_PWD_FORMAT = PARTI_FORMAT + "pwd"; | |||||
private static final String BINDING_ADDRESS_FORMAT = PARTI_FORMAT + "address"; | |||||
private static final String DB_FORMAT = LEDGER_BINDING_FORMAT + "db."; | |||||
private static final String BINDING_DB_URI_FORMAT = DB_FORMAT + "uri"; | |||||
private static final String BINDING_DB_PWD_FORMAT = DB_FORMAT + "pwd"; | |||||
private PeerNodeRunner[] peerNodes; | |||||
private GatewayNodeRunner gatewayNodeRunner; | |||||
public void start(int nodeSize) throws Exception { | |||||
HashDigest ledgerHash = ledgerInit(nodeSize); | |||||
// 启动Peer节点 | |||||
peerNodes = peerNodeStart(nodeSize, ledgerHash); | |||||
// 启动网关节点 | |||||
gatewayNodeRunner = gatewayNodeStart(peerNodes[0].getServiceAddress()); | |||||
} | |||||
public LedgerInitProperties initLedgerProperties(int nodeSize) throws Exception { | |||||
File ledgerInitFile = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX + | |||||
String.format(MockerConstant.LEDGER_INIT_FORMATTER, nodeSize)); | |||||
final LedgerInitProperties ledgerInitProperties = LedgerInitProperties.resolve(new FileInputStream(ledgerInitFile)); | |||||
return ledgerInitProperties; | |||||
} | |||||
private HashDigest ledgerInit(int nodeSize) throws Exception { | |||||
System.out.println("----------- is daemon=" + Thread.currentThread().isDaemon()); | |||||
Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); | |||||
LedgerInitProperties initSetting = initLedgerProperties(nodeSize); | |||||
Set<HashDigest> hashDigestSet = new HashSet<>(); | |||||
CountDownLatch quitLatch = new CountDownLatch(nodeSize); | |||||
ExecutorService peerThreads = Executors.newFixedThreadPool(nodeSize); | |||||
for (int i = 0; i < nodeSize; i++) { | |||||
final int nodeIndex = i; | |||||
peerThreads.execute(() -> { | |||||
// 启动服务器; | |||||
NetworkAddress initAddr = initSetting.getConsensusParticipant(nodeIndex).getInitializerAddress(); | |||||
NodeWebContext node = new NodeWebContext(nodeIndex, initAddr); | |||||
PrivKey privkey = KeyGenCommand.decodePrivKeyWithRawPassword(PRIVATE_KEYS[nodeIndex], PASSWORD); | |||||
DBConnectionConfig dbConn = new DBConnectionConfig(); | |||||
dbConn.setConnectionUri(MockerConstant.DB_MEMS[nodeIndex]); | |||||
ThreadInvoker.AsyncCallback<HashDigest> nodeCallback = node.startInit(privkey, initSetting, dbConn, consolePrompter, | |||||
quitLatch); | |||||
hashDigestSet.add(nodeCallback.waitReturn()); | |||||
}); | |||||
} | |||||
quitLatch.await(); | |||||
if (hashDigestSet.size() != 1) { | |||||
throw new IllegalStateException(String.format("%s Node Ledger Init Fail !!!", nodeSize)); | |||||
} | |||||
return hashDigestSet.toArray(new HashDigest[hashDigestSet.size()])[0]; | |||||
} | |||||
public PeerNodeRunner[] peerNodeStart(int nodeSize, HashDigest ledgerHash) { | |||||
int portStart = PEER_PORT_START; | |||||
List<ThreadInvoker.AsyncCallback<Object>> threadInvokers = new ArrayList<>(); | |||||
final PeerNodeRunner[] peerNodeRunners = new PeerNodeRunner[nodeSize]; | |||||
for (int i = 0; i < nodeSize; i++) { | |||||
NetworkAddress peerSrvAddr = new NetworkAddress(LOCAL_ADDRESS, portStart); | |||||
LedgerBindingConfig bindingConfig = loadBindingConfig(i, ledgerHash); | |||||
PeerNodeRunner peerNodeRunner = new PeerNodeRunner(peerSrvAddr, bindingConfig); | |||||
peerNodeRunners[i] = peerNodeRunner; | |||||
portStart += 10; | |||||
threadInvokers.add(peerNodeRunner.start()); | |||||
} | |||||
// 等待结果 | |||||
for (ThreadInvoker.AsyncCallback<Object> threadInvoker : threadInvokers) { | |||||
threadInvoker.waitReturn(); | |||||
} | |||||
return peerNodeRunners; | |||||
} | |||||
public GatewayNodeRunner gatewayNodeStart(NetworkAddress peerAddress) { | |||||
GatewayConfigProperties.KeyPairConfig gwKeyPair = new GatewayConfigProperties.KeyPairConfig(); | |||||
gwKeyPair.setPubKeyValue(PUBLIC_KEYS[0]); | |||||
gwKeyPair.setPrivKeyValue(PRIVATE_KEYS[0]); | |||||
gwKeyPair.setPrivKeyPassword(PASSWORD_ENCODE); | |||||
GatewayNodeRunner gateway = new GatewayNodeRunner(LOCAL_ADDRESS, GATEWAY_PORT, gwKeyPair, | |||||
peerAddress, CONSENSUS_PROVIDERS,null); | |||||
ThreadInvoker.AsyncCallback<Object> gwStarting = gateway.start(); | |||||
gwStarting.waitReturn(); | |||||
return gateway; | |||||
} | |||||
public void stop() { | |||||
if (peerNodes != null) { | |||||
for (PeerNodeRunner peerNodeRunner : peerNodes) { | |||||
peerNodeRunner.stop(); | |||||
} | |||||
} | |||||
if (gatewayNodeRunner != null) { | |||||
gatewayNodeRunner.stop(); | |||||
} | |||||
} | |||||
private LedgerBindingConfig loadBindingConfig(int nodeIndex, HashDigest ledgerHash) { | |||||
Properties properties = new Properties(); | |||||
String ledgerHashBase58 = ledgerHash.toBase58(); | |||||
properties.put("ledger.bindings", ledgerHashBase58); | |||||
properties.put(String.format(BINDING_ID_FORMAT, ledgerHashBase58), nodeIndex); | |||||
properties.put(String.format(BINDING_PK_PATH_FORMAT, ledgerHashBase58), ""); | |||||
properties.put(String.format(BINDING_PK_FORMAT, ledgerHashBase58), PUBLIC_KEYS[nodeIndex]); | |||||
properties.put(String.format(BINDING_PWD_FORMAT, ledgerHashBase58), PASSWORD_ENCODE); | |||||
properties.put(String.format(BINDING_ADDRESS_FORMAT, ledgerHashBase58), ADDRESS_ARRAY[nodeIndex]); | |||||
properties.put(String.format(BINDING_DB_URI_FORMAT, ledgerHashBase58), DB_MEMS[nodeIndex]); | |||||
properties.put(String.format(BINDING_DB_PWD_FORMAT, ledgerHashBase58), ""); | |||||
return LedgerBindingConfig.resolve(properties); | |||||
} | |||||
} |
@@ -0,0 +1,51 @@ | |||||
package com.jd.blockchain.mocker.handler; | |||||
import com.jd.blockchain.ledger.core.LedgerException; | |||||
import com.jd.blockchain.ledger.core.OperationHandle; | |||||
import com.jd.blockchain.ledger.core.impl.OperationHandleRegisteration; | |||||
import com.jd.blockchain.ledger.core.impl.handles.*; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class MockerOperationHandleRegister implements OperationHandleRegisteration { | |||||
private List<OperationHandle> opHandles = new ArrayList<>(); | |||||
public MockerOperationHandleRegister() { | |||||
initDefaultHandles(); | |||||
} | |||||
/** | |||||
* 针对不采用bean依赖注入的方式来处理; | |||||
*/ | |||||
private void initDefaultHandles(){ | |||||
opHandles.add(new DataAccountKVSetOperationHandle()); | |||||
opHandles.add(new DataAccountRegisterOperationHandle()); | |||||
opHandles.add(new UserRegisterOperationHandle()); | |||||
opHandles.add(new ContractCodeDeployOperationHandle()); | |||||
// opHandles.add(new ContractEventSendOperationHandle()); | |||||
} | |||||
public List<OperationHandle> getOpHandles() { | |||||
return opHandles; | |||||
} | |||||
public void registerHandler(OperationHandle operationHandle) { | |||||
opHandles.add(operationHandle); | |||||
} | |||||
public void removeHandler(OperationHandle operationHandle) { | |||||
opHandles.remove(operationHandle); | |||||
} | |||||
@Override | |||||
public OperationHandle getHandle(Class<?> operationType) { | |||||
for (OperationHandle handle : opHandles) { | |||||
if (handle.support(operationType)) { | |||||
return handle; | |||||
} | |||||
} | |||||
throw new LedgerException("Unsupported operation type[" + operationType.getName() + "]!"); | |||||
} | |||||
} |
@@ -0,0 +1,216 @@ | |||||
package com.jd.blockchain.mocker.handler; | |||||
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.mocker.config.MockerConstant; | |||||
import com.jd.blockchain.mocker.data.KvData; | |||||
import com.jd.blockchain.mocker.data.ResponseData; | |||||
import com.jd.blockchain.sdk.BlockchainService; | |||||
import com.jd.blockchain.sdk.client.GatewayServiceFactory; | |||||
import com.jd.blockchain.tools.keygen.KeyGenCommand; | |||||
public class MockerServiceHandler { | |||||
private BlockchainKeypair defaultParticipant; | |||||
private BlockchainKeypair defaultUser; | |||||
private BlockchainKeypair defaultDataAccount; | |||||
private String gatewayHost; | |||||
private int gatewayPort; | |||||
private GatewayServiceFactory gatewayServiceFactory; | |||||
private BlockchainService blockchainService; | |||||
private HashDigest ledgerHash; | |||||
public MockerServiceHandler(String gatewayHost, int gatewayPort) { | |||||
this.gatewayHost = gatewayHost; | |||||
this.gatewayPort = gatewayPort; | |||||
init(); | |||||
} | |||||
public BlockchainKeypair getDefaultParticipant() { | |||||
return defaultParticipant; | |||||
} | |||||
public BlockchainKeypair getDefaultUser() { | |||||
return defaultUser; | |||||
} | |||||
public BlockchainKeypair getDefaultDataAccount() { | |||||
return defaultDataAccount; | |||||
} | |||||
public String getDefaultDataAccountAddress() { | |||||
return defaultDataAccount.getAddress().toBase58(); | |||||
} | |||||
public String getGatewayHost() { | |||||
return gatewayHost; | |||||
} | |||||
public int getGatewayPort() { | |||||
return gatewayPort; | |||||
} | |||||
public GatewayServiceFactory getGatewayServiceFactory() { | |||||
return gatewayServiceFactory; | |||||
} | |||||
public BlockchainService getBlockchainService() { | |||||
return blockchainService; | |||||
} | |||||
public HashDigest getLedgerHash() { | |||||
return ledgerHash; | |||||
} | |||||
private void init() { | |||||
defaultParticipant = defaultParticipant(); | |||||
gatewayServiceFactory = GatewayServiceFactory.connect(gatewayHost, gatewayPort, | |||||
false, defaultParticipant); | |||||
blockchainService = gatewayServiceFactory.getBlockchainService(); | |||||
HashDigest[] ledgerHashs = blockchainService.getLedgerHashs(); | |||||
ledgerHash = ledgerHashs[0]; | |||||
// 默认注册部分内容 | |||||
// 注册一个用户和一个数据账户 | |||||
TransactionTemplate txTemplate = blockchainService.newTransaction(ledgerHash); | |||||
defaultUser = newKeypair(); | |||||
defaultDataAccount = newKeypair(); | |||||
// 注册用户 | |||||
txTemplate.users().register(defaultUser.getIdentity()); | |||||
// 注册数据账户 | |||||
txTemplate.dataAccounts().register(defaultDataAccount.getIdentity()); | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemplate.prepare(); | |||||
// 使用私钥进行签名; | |||||
prepTx.sign(defaultParticipant); | |||||
// 提交交易; | |||||
TransactionResponse txResponse = prepTx.commit(); | |||||
System.out.printf("Commit Transaction Result = %s \r\n", txResponse.isSuccess()); | |||||
} | |||||
public BlockchainKeypair newKeypair() { | |||||
return BlockchainKeyGenerator.getInstance().generate(); | |||||
} | |||||
private BlockchainKeypair defaultParticipant() { | |||||
PrivKey privKey = KeyGenCommand.decodePrivKeyWithRawPassword(MockerConstant.PRIVATE_KEYS[0], MockerConstant.PASSWORD); | |||||
PubKey pubKey = KeyGenCommand.decodePubKey(MockerConstant.PUBLIC_KEYS[0]); | |||||
return new BlockchainKeypair(pubKey, privKey); | |||||
} | |||||
public ResponseData<KvData> writeKv(String key, byte[] value) { | |||||
return writeKv(key, value, -1); | |||||
} | |||||
public ResponseData<KvData> writeKv(String key, byte[] value, long version) { | |||||
return writeKv(getDefaultDataAccountAddress(), key, value, version); | |||||
} | |||||
public ResponseData<KvData> writeKv(String dataAccount, String key, byte[] value) { | |||||
return writeKv(dataAccount, key, value, -1); | |||||
} | |||||
public ResponseData<KvData> writeKv(String dataAccount, String key, byte[] value, long version) { | |||||
TransactionTemplate txTemplate = newTxTemplate(); | |||||
txTemplate.dataAccount(dataAccount).set(key, value, version); | |||||
TransactionResponse txResponse = txPrepareAndCommit(txTemplate); | |||||
long saveVersion = version; | |||||
if (txResponse.isSuccess()) { | |||||
saveVersion = version + 1; | |||||
} | |||||
KvData kvData = new KvData(dataAccount, key, value, saveVersion); | |||||
return new ResponseData(txResponse, kvData); | |||||
} | |||||
public ResponseData<BlockchainKeypair> registerUser() { | |||||
BlockchainKeypair newUser = BlockchainKeyGenerator.getInstance().generate(); | |||||
return registerUser(newUser); | |||||
} | |||||
public ResponseData<BlockchainKeypair> registerUser(BlockchainKeypair user) { | |||||
TransactionTemplate txTemplate = newTxTemplate(); | |||||
// 注册 | |||||
txTemplate.users().register(user.getIdentity()); | |||||
TransactionResponse txResponse = txPrepareAndCommit(txTemplate); | |||||
return new ResponseData(txResponse, user); | |||||
} | |||||
public ResponseData<BlockchainKeypair> registerDataAccount() { | |||||
BlockchainKeypair newDataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||||
return registerDataAccount(newDataAccount); | |||||
} | |||||
public ResponseData<BlockchainKeypair> registerDataAccount(BlockchainKeypair dataAccount) { | |||||
TransactionTemplate txTemplate = newTxTemplate(); | |||||
// 注册 | |||||
txTemplate.dataAccounts().register(dataAccount.getIdentity()); | |||||
TransactionResponse txResponse = txPrepareAndCommit(txTemplate); | |||||
return new ResponseData(txResponse, dataAccount); | |||||
} | |||||
public ResponseData<Object> deployContract() { | |||||
return null; | |||||
} | |||||
public ResponseData<Object> executeContract() { | |||||
return null; | |||||
} | |||||
private TransactionTemplate newTxTemplate() { | |||||
return blockchainService.newTransaction(ledgerHash); | |||||
} | |||||
private TransactionResponse txPrepareAndCommit(TransactionTemplate txTemplate) { | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemplate.prepare(); | |||||
// 使用私钥进行签名; | |||||
prepTx.sign(defaultParticipant); | |||||
// 提交交易; | |||||
TransactionResponse txResponse = prepTx.commit(); | |||||
return txResponse; | |||||
} | |||||
} |
@@ -0,0 +1,75 @@ | |||||
package com.jd.blockchain.mocker.node; | |||||
import com.jd.blockchain.gateway.GatewayConfigProperties; | |||||
import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; | |||||
import com.jd.blockchain.gateway.GatewayServerBooter; | |||||
import com.jd.blockchain.utils.concurrent.ThreadInvoker; | |||||
import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; | |||||
import com.jd.blockchain.utils.net.NetworkAddress; | |||||
import org.springframework.core.io.ClassPathResource; | |||||
import org.springframework.util.CollectionUtils; | |||||
import java.util.Map; | |||||
public class GatewayNodeRunner { | |||||
private NetworkAddress serviceAddress; | |||||
private GatewayServerBooter gatewayServer; | |||||
public GatewayNodeRunner(String host, int port, KeyPairConfig gatewayDefaultKey, NetworkAddress masterPeerAddres) { | |||||
this(host, port, gatewayDefaultKey, masterPeerAddres, null,null); | |||||
} | |||||
public GatewayNodeRunner(String host, int port, KeyPairConfig gatewayDefaultKey, NetworkAddress masterPeerAddres, String[] providers, | |||||
Map<String,Object> otherMap) { | |||||
this.serviceAddress = new NetworkAddress(host, port); | |||||
GatewayConfigProperties config = new GatewayConfigProperties(); | |||||
config.http().setHost(host); | |||||
config.http().setPort(port); | |||||
if (providers != null) { | |||||
for (String provider : providers) { | |||||
config.providerConfig().add(provider); | |||||
} | |||||
} | |||||
config.setMasterPeerAddress(masterPeerAddres); | |||||
config.keys().getDefault().setPubKeyValue(gatewayDefaultKey.getPubKeyValue()); | |||||
config.keys().getDefault().setPrivKeyValue(gatewayDefaultKey.getPrivKeyValue()); | |||||
config.keys().getDefault().setPrivKeyPassword(gatewayDefaultKey.getPrivKeyPassword()); | |||||
if(!CollectionUtils.isEmpty(otherMap)){ | |||||
config.setDataRetrievalUrl(otherMap.get("DATA_RETRIEVAL_URL").toString()); | |||||
} | |||||
//get the springConfigLocation; | |||||
ClassPathResource configResource = new ClassPathResource("application-gw.properties"); | |||||
String springConfigLocation = "classPath:"+configResource.getPath(); | |||||
this.gatewayServer = new GatewayServerBooter(config,springConfigLocation); | |||||
} | |||||
public AsyncCallback<Object> start() { | |||||
ThreadInvoker<Object> invoker = new ThreadInvoker<Object>() { | |||||
@Override | |||||
protected Object invoke() throws Exception { | |||||
gatewayServer.start(); | |||||
return null; | |||||
} | |||||
}; | |||||
return invoker.start(); | |||||
} | |||||
public void stop() { | |||||
gatewayServer.close(); | |||||
} | |||||
public NetworkAddress getServiceAddress() { | |||||
return serviceAddress; | |||||
} | |||||
} |
@@ -0,0 +1,106 @@ | |||||
package com.jd.blockchain.mocker.node; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.crypto.PrivKey; | |||||
import com.jd.blockchain.ledger.TransactionContent; | |||||
import com.jd.blockchain.ledger.core.LedgerInitDecision; | |||||
import com.jd.blockchain.ledger.core.LedgerInitPermission; | |||||
import com.jd.blockchain.ledger.core.LedgerRepository; | |||||
import com.jd.blockchain.ledger.core.impl.LedgerManager; | |||||
import com.jd.blockchain.mocker.config.LedgerInitWebConfiguration; | |||||
import com.jd.blockchain.storage.service.DbConnection; | |||||
import com.jd.blockchain.storage.service.impl.composite.CompositeConnectionFactory; | |||||
import com.jd.blockchain.tools.initializer.DBConnectionConfig; | |||||
import com.jd.blockchain.tools.initializer.LedgerInitProcess; | |||||
import com.jd.blockchain.tools.initializer.LedgerInitProperties; | |||||
import com.jd.blockchain.tools.initializer.Prompter; | |||||
import com.jd.blockchain.tools.initializer.web.LedgerInitializeWebController; | |||||
import com.jd.blockchain.utils.concurrent.ThreadInvoker; | |||||
import com.jd.blockchain.utils.net.NetworkAddress; | |||||
import org.springframework.boot.SpringApplication; | |||||
import org.springframework.context.ConfigurableApplicationContext; | |||||
import java.util.concurrent.CountDownLatch; | |||||
public class NodeWebContext { | |||||
private NetworkAddress serverAddress; | |||||
private DBConnectionConfig dbConnConfig; | |||||
private volatile ConfigurableApplicationContext ctx; | |||||
private volatile LedgerInitProcess initProcess; | |||||
private volatile LedgerInitializeWebController controller; | |||||
private volatile LedgerManager ledgerManager; | |||||
private volatile CompositeConnectionFactory db; | |||||
private int id; | |||||
public int getId() { | |||||
return controller.getId(); | |||||
} | |||||
public TransactionContent getInitTxContent() { | |||||
return controller.getInitTxContent(); | |||||
} | |||||
public LedgerInitPermission getLocalPermission() { | |||||
return controller.getLocalPermission(); | |||||
} | |||||
public LedgerInitDecision getLocalDecision() { | |||||
return controller.getLocalDecision(); | |||||
} | |||||
public NodeWebContext(int id, NetworkAddress serverAddress) { | |||||
this.id = id; | |||||
this.serverAddress = serverAddress; | |||||
} | |||||
public LedgerRepository registLedger(HashDigest ledgerHash) { | |||||
DbConnection conn = db.connect(dbConnConfig.getUri()); | |||||
LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, conn.getStorageService()); | |||||
return ledgerRepo; | |||||
} | |||||
public ThreadInvoker.AsyncCallback<HashDigest> startInit(PrivKey privKey, LedgerInitProperties setting, | |||||
DBConnectionConfig dbConnConfig, Prompter prompter, CountDownLatch quitLatch) { | |||||
ThreadInvoker<HashDigest> invoker = new ThreadInvoker<HashDigest>() { | |||||
@Override | |||||
protected HashDigest invoke() throws Exception { | |||||
doStartServer(); | |||||
NodeWebContext.this.dbConnConfig = dbConnConfig; | |||||
HashDigest ledgerHash = NodeWebContext.this.initProcess.initialize(id, privKey, setting, | |||||
dbConnConfig, prompter); | |||||
System.out.printf("ledgerHash = %s \r\n", ledgerHash.toBase58()); | |||||
quitLatch.countDown(); | |||||
return ledgerHash; | |||||
} | |||||
}; | |||||
return invoker.start(); | |||||
} | |||||
public void doStartServer() { | |||||
String argServerAddress = String.format("--server.address=%s", serverAddress.getHost()); | |||||
String argServerPort = String.format("--server.port=%s", serverAddress.getPort()); | |||||
String nodebug = "--debug=false"; | |||||
String[] innerArgs = { argServerAddress, argServerPort, nodebug }; | |||||
ctx = SpringApplication.run(LedgerInitWebConfiguration.class, innerArgs); | |||||
ctx.setId("Node-" + id); | |||||
controller = ctx.getBean(LedgerInitializeWebController.class); | |||||
ledgerManager = ctx.getBean(LedgerManager.class); | |||||
db = ctx.getBean(CompositeConnectionFactory.class); | |||||
initProcess = ctx.getBean(LedgerInitProcess.class); | |||||
} | |||||
} |
@@ -0,0 +1,62 @@ | |||||
package com.jd.blockchain.mocker.node; | |||||
import com.jd.blockchain.peer.PeerServerBooter; | |||||
import com.jd.blockchain.storage.service.DbConnectionFactory; | |||||
import com.jd.blockchain.tools.initializer.LedgerBindingConfig; | |||||
import com.jd.blockchain.utils.concurrent.ThreadInvoker; | |||||
import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; | |||||
import com.jd.blockchain.utils.net.NetworkAddress; | |||||
public class PeerNodeRunner { | |||||
private NetworkAddress serviceAddress; | |||||
private volatile PeerServerBooter peerServer; | |||||
private LedgerBindingConfig ledgerBindingConfig; | |||||
public DbConnectionFactory getDBConnectionFactory() { | |||||
return peerServer.getDBConnectionFactory(); | |||||
} | |||||
public NetworkAddress getServiceAddress() { | |||||
return serviceAddress; | |||||
} | |||||
public LedgerBindingConfig getLedgerBindingConfig() { | |||||
return ledgerBindingConfig; | |||||
} | |||||
public PeerNodeRunner(NetworkAddress serviceAddress, LedgerBindingConfig ledgerBindingConfig) { | |||||
this(serviceAddress, ledgerBindingConfig, null); | |||||
} | |||||
public PeerNodeRunner(NetworkAddress serviceAddress, LedgerBindingConfig ledgerBindingConfig, | |||||
DbConnectionFactory dbConnectionFactory) { | |||||
this.serviceAddress = serviceAddress; | |||||
this.ledgerBindingConfig = ledgerBindingConfig; | |||||
if (dbConnectionFactory == null) { | |||||
this.peerServer = new PeerServerBooter(ledgerBindingConfig, serviceAddress.getHost(), serviceAddress.getPort(),null); | |||||
}else { | |||||
this.peerServer = new PeerServerBooter(ledgerBindingConfig, serviceAddress.getHost(), serviceAddress.getPort(),null, | |||||
dbConnectionFactory); | |||||
} | |||||
} | |||||
public AsyncCallback<Object> start() { | |||||
ThreadInvoker<Object> invoker = new ThreadInvoker<Object>() { | |||||
@Override | |||||
protected Object invoke() throws Exception { | |||||
peerServer.start(); | |||||
return null; | |||||
} | |||||
}; | |||||
return invoker.start(); | |||||
} | |||||
public void stop() { | |||||
peerServer.close(); | |||||
} | |||||
} |
@@ -0,0 +1,80 @@ | |||||
package com.jd.blockchain.mocker.proxy; | |||||
import com.jd.blockchain.contract.Contract; | |||||
import com.jd.blockchain.contract.ContractEvent; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | |||||
import com.jd.blockchain.mocker.MockerNodeContext; | |||||
import com.jd.blockchain.mocker.handler.MockerContractExeHandle; | |||||
import com.jd.blockchain.transaction.TxBuilder; | |||||
import java.lang.annotation.Annotation; | |||||
import java.lang.reflect.InvocationHandler; | |||||
import java.lang.reflect.Method; | |||||
public class ContractProxy<T> implements InvocationHandler { | |||||
private BlockchainIdentity identity; | |||||
private MockerNodeContext mockerNodeContext; | |||||
private T instance; | |||||
private MockerContractExeHandle operationHandle; | |||||
public ContractProxy(BlockchainIdentity identity, MockerNodeContext mockerNodeContext, | |||||
T instance, MockerContractExeHandle operationHandle) { | |||||
this.identity = identity; | |||||
this.mockerNodeContext = mockerNodeContext; | |||||
this.instance = instance; | |||||
this.operationHandle = operationHandle; | |||||
} | |||||
@Override | |||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |||||
// 实际执行时,首先判断执行的是否是添加注解的方法 | |||||
if (!isExecuteContractMethod(method)) { | |||||
return method.invoke(instance, args); | |||||
} | |||||
// 首先发送一次执行的请求 | |||||
TxBuilder txBuilder = mockerNodeContext.txBuilder(); | |||||
Class<?> contractInft = null; | |||||
Class<?>[] instanceInfts = instance.getClass().getInterfaces(); | |||||
for (Class<?> instanceInft : instanceInfts) { | |||||
if (instanceInft.isAnnotationPresent(Contract.class)) { | |||||
contractInft = instanceInft; | |||||
break; | |||||
} | |||||
} | |||||
if (contractInft == null) { | |||||
throw new IllegalStateException("This object does not implement the interface for the @Contract annotation !!!"); | |||||
} | |||||
// 生成代理类 | |||||
Object proxyInstance = txBuilder.contract(identity.getAddress().toBase58(), contractInft); | |||||
// 代理方式执行一次 | |||||
method.invoke(proxyInstance, args); | |||||
TransactionRequest txRequest = mockerNodeContext.txRequest(txBuilder); | |||||
// 放入到Map中 | |||||
HashDigest txHash = txRequest.getTransactionContent().getHash(); | |||||
operationHandle.registerExecutorProxy(txHash, new ExecutorProxy(instance, method, args)); | |||||
// 提交该请求至整个区块链系统 | |||||
mockerNodeContext.txProcess(txRequest); | |||||
// 不处理返回值 | |||||
return null; | |||||
} | |||||
private boolean isExecuteContractMethod(Method method) { | |||||
Annotation annotation = method.getAnnotation(ContractEvent.class); | |||||
return annotation != null; | |||||
} | |||||
} |
@@ -0,0 +1,34 @@ | |||||
package com.jd.blockchain.mocker.proxy; | |||||
import java.lang.reflect.Method; | |||||
public class ExecutorProxy { | |||||
private Object instance; | |||||
private Method method; | |||||
private Object[] args; | |||||
public ExecutorProxy(Object instance, Method method, Object[] args) { | |||||
this.instance = instance; | |||||
this.method = method; | |||||
this.args = args; | |||||
} | |||||
public Object getInstance() { | |||||
return instance; | |||||
} | |||||
public Method getMethod() { | |||||
return method; | |||||
} | |||||
public Object[] getArgs() { | |||||
return args; | |||||
} | |||||
public Object invoke() throws Exception { | |||||
return method.invoke(instance, args); | |||||
} | |||||
} |
@@ -0,0 +1,167 @@ | |||||
# Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags | |||||
# | |||||
# Licensed under the Apache License, Version 2.0 (the "License"); | |||||
# you may not use this file except in compliance with the License. | |||||
# You may obtain a copy of the License at | |||||
# | |||||
# http://www.apache.org/licenses/LICENSE-2.0 | |||||
# | |||||
# Unless required by applicable law or agreed to in writing, software | |||||
# distributed under the License is distributed on an "AS IS" BASIS, | |||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
# See the License for the specific language governing permissions and | |||||
# limitations under the License. | |||||
############################################ | |||||
###### Consensus Commit Block Parameters: transaction count ###### | |||||
############################################ | |||||
system.block.txsize=500 | |||||
############################################ | |||||
###### Consensus Commit Block Parameters: delay time ###### | |||||
############################################ | |||||
system.block.maxdelay=5000 | |||||
############################################ | |||||
###### #Consensus Participant0 ###### | |||||
############################################ | |||||
system.server.0.pubkey=3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9 | |||||
system.server.0.network.host=127.0.0.1 | |||||
system.server.0.network.port=16000 | |||||
system.server.0.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant1 ###### | |||||
############################################ | |||||
system.server.1.pubkey=3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX | |||||
system.server.1.network.host=127.0.0.1 | |||||
system.server.1.network.port=16100 | |||||
system.server.1.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant2 ###### | |||||
############################################ | |||||
system.server.2.pubkey=3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x | |||||
system.server.2.network.host=127.0.0.1 | |||||
system.server.2.network.port=16200 | |||||
system.server.2.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant3 ###### | |||||
############################################ | |||||
system.server.3.pubkey=3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk | |||||
system.server.3.network.host=127.0.0.1 | |||||
system.server.3.network.port=16300 | |||||
system.server.3.network.secure=false | |||||
############################################ | |||||
####### Communication Configurations ####### | |||||
############################################ | |||||
#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) | |||||
#This parameter is not currently being used | |||||
#system.authentication.hmacAlgorithm = HmacSHA1 | |||||
#Specify if the communication system should use a thread to send data (true or false) | |||||
system.communication.useSenderThread = true | |||||
#Force all processes to use the same public/private keys pair and secret key. This is useful when deploying experiments | |||||
#and benchmarks, but must not be used in production systems. | |||||
system.communication.defaultkeys = true | |||||
############################################ | |||||
### Replication Algorithm Configurations ### | |||||
############################################ | |||||
#Number of servers in the group | |||||
system.servers.num = 4 | |||||
#Maximum number of faulty replicas | |||||
system.servers.f = 1 | |||||
#Timeout to asking for a client request | |||||
system.totalordermulticast.timeout = 2000 | |||||
#Maximum batch size (in number of messages) | |||||
system.totalordermulticast.maxbatchsize = 500 | |||||
#Number of nonces (for non-determinism actions) generated | |||||
system.totalordermulticast.nonces = 10 | |||||
#if verification of leader-generated timestamps are increasing | |||||
#it can only be used on systems in which the network clocks | |||||
#are synchronized | |||||
system.totalordermulticast.verifyTimestamps = false | |||||
#Quantity of messages that can be stored in the receive queue of the communication system | |||||
system.communication.inQueueSize = 500000 | |||||
# Quantity of messages that can be stored in the send queue of each replica | |||||
system.communication.outQueueSize = 500000 | |||||
#Set to 1 if SMaRt should use signatures, set to 0 if otherwise | |||||
system.communication.useSignatures = 0 | |||||
#Set to 1 if SMaRt should use MAC's, set to 0 if otherwise | |||||
system.communication.useMACs = 1 | |||||
#Set to 1 if SMaRt should use the standard output to display debug messages, set to 0 if otherwise | |||||
system.debug = 0 | |||||
#Print information about the replica when it is shutdown | |||||
system.shutdownhook = true | |||||
############################################ | |||||
###### State Transfer Configurations ####### | |||||
############################################ | |||||
#Activate the state transfer protocol ('true' to activate, 'false' to de-activate) | |||||
system.totalordermulticast.state_transfer = true | |||||
#Maximum ahead-of-time message not discarded | |||||
system.totalordermulticast.highMark = 10000 | |||||
#Maximum ahead-of-time message not discarded when the replica is still on EID 0 (after which the state transfer is triggered) | |||||
system.totalordermulticast.revival_highMark = 10 | |||||
#Number of ahead-of-time messages necessary to trigger the state transfer after a request timeout occurs | |||||
system.totalordermulticast.timeout_highMark = 200 | |||||
############################################ | |||||
###### Log and Checkpoint Configurations ### | |||||
############################################ | |||||
system.totalordermulticast.log = true | |||||
system.totalordermulticast.log_parallel = false | |||||
system.totalordermulticast.log_to_disk = false | |||||
system.totalordermulticast.sync_log = false | |||||
#Period at which BFT-SMaRt requests the state to the application (for the state transfer state protocol) | |||||
system.totalordermulticast.checkpoint_period = 1000 | |||||
system.totalordermulticast.global_checkpoint_period = 120000 | |||||
system.totalordermulticast.checkpoint_to_disk = false | |||||
system.totalordermulticast.sync_ckp = false | |||||
############################################ | |||||
###### Reconfiguration Configurations ###### | |||||
############################################ | |||||
#Replicas ID for the initial view, separated by a comma. | |||||
# The number of replicas in this parameter should be equal to that specified in 'system.servers.num' | |||||
system.initial.view = 0,1,2,3 | |||||
#The ID of the trust third party (TTP) | |||||
system.ttp.id = 7002 | |||||
#This sets if the system will function in Byzantine or crash-only mode. Set to "true" to support Byzantine faults | |||||
system.bft = true | |||||
#Custom View Storage; | |||||
#view.storage.handler=bftsmart.reconfiguration.views.DefaultViewStorage |
@@ -0,0 +1,167 @@ | |||||
# Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags | |||||
# | |||||
# Licensed under the Apache License, Version 2.0 (the "License"); | |||||
# you may not use this file except in compliance with the License. | |||||
# You may obtain a copy of the License at | |||||
# | |||||
# http://www.apache.org/licenses/LICENSE-2.0 | |||||
# | |||||
# Unless required by applicable law or agreed to in writing, software | |||||
# distributed under the License is distributed on an "AS IS" BASIS, | |||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
# See the License for the specific language governing permissions and | |||||
# limitations under the License. | |||||
############################################ | |||||
###### Consensus Commit Block Parameters: transaction count ###### | |||||
############################################ | |||||
system.block.txsize=500 | |||||
############################################ | |||||
###### Consensus Commit Block Parameters: delay time ###### | |||||
############################################ | |||||
system.block.maxdelay=5000 | |||||
############################################ | |||||
###### #Consensus Participant0 ###### | |||||
############################################ | |||||
system.server.0.pubkey=3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9 | |||||
system.server.0.network.host=127.0.0.1 | |||||
system.server.0.network.port=16000 | |||||
system.server.0.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant1 ###### | |||||
############################################ | |||||
system.server.1.pubkey=3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX | |||||
system.server.1.network.host=127.0.0.1 | |||||
system.server.1.network.port=16100 | |||||
system.server.1.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant2 ###### | |||||
############################################ | |||||
system.server.2.pubkey=3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x | |||||
system.server.2.network.host=127.0.0.1 | |||||
system.server.2.network.port=16200 | |||||
system.server.2.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant3 ###### | |||||
############################################ | |||||
system.server.3.pubkey=3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk | |||||
system.server.3.network.host=127.0.0.1 | |||||
system.server.3.network.port=16300 | |||||
system.server.3.network.secure=false | |||||
############################################ | |||||
####### Communication Configurations ####### | |||||
############################################ | |||||
#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) | |||||
#This parameter is not currently being used | |||||
#system.authentication.hmacAlgorithm = HmacSHA1 | |||||
#Specify if the communication system should use a thread to send data (true or false) | |||||
system.communication.useSenderThread = true | |||||
#Force all processes to use the same public/private keys pair and secret key. This is useful when deploying experiments | |||||
#and benchmarks, but must not be used in production systems. | |||||
system.communication.defaultkeys = true | |||||
############################################ | |||||
### Replication Algorithm Configurations ### | |||||
############################################ | |||||
#Number of servers in the group | |||||
system.servers.num = 4 | |||||
#Maximum number of faulty replicas | |||||
system.servers.f = 1 | |||||
#Timeout to asking for a client request | |||||
system.totalordermulticast.timeout = 2000 | |||||
#Maximum batch size (in number of messages) | |||||
system.totalordermulticast.maxbatchsize = 500 | |||||
#Number of nonces (for non-determinism actions) generated | |||||
system.totalordermulticast.nonces = 10 | |||||
#if verification of leader-generated timestamps are increasing | |||||
#it can only be used on systems in which the network clocks | |||||
#are synchronized | |||||
system.totalordermulticast.verifyTimestamps = false | |||||
#Quantity of messages that can be stored in the receive queue of the communication system | |||||
system.communication.inQueueSize = 500000 | |||||
# Quantity of messages that can be stored in the send queue of each replica | |||||
system.communication.outQueueSize = 500000 | |||||
#Set to 1 if SMaRt should use signatures, set to 0 if otherwise | |||||
system.communication.useSignatures = 0 | |||||
#Set to 1 if SMaRt should use MAC's, set to 0 if otherwise | |||||
system.communication.useMACs = 1 | |||||
#Set to 1 if SMaRt should use the standard output to display debug messages, set to 0 if otherwise | |||||
system.debug = 0 | |||||
#Print information about the replica when it is shutdown | |||||
system.shutdownhook = true | |||||
############################################ | |||||
###### State Transfer Configurations ####### | |||||
############################################ | |||||
#Activate the state transfer protocol ('true' to activate, 'false' to de-activate) | |||||
system.totalordermulticast.state_transfer = true | |||||
#Maximum ahead-of-time message not discarded | |||||
system.totalordermulticast.highMark = 10000 | |||||
#Maximum ahead-of-time message not discarded when the replica is still on EID 0 (after which the state transfer is triggered) | |||||
system.totalordermulticast.revival_highMark = 10 | |||||
#Number of ahead-of-time messages necessary to trigger the state transfer after a request timeout occurs | |||||
system.totalordermulticast.timeout_highMark = 200 | |||||
############################################ | |||||
###### Log and Checkpoint Configurations ### | |||||
############################################ | |||||
system.totalordermulticast.log = true | |||||
system.totalordermulticast.log_parallel = false | |||||
system.totalordermulticast.log_to_disk = false | |||||
system.totalordermulticast.sync_log = false | |||||
#Period at which BFT-SMaRt requests the state to the application (for the state transfer state protocol) | |||||
system.totalordermulticast.checkpoint_period = 1000 | |||||
system.totalordermulticast.global_checkpoint_period = 120000 | |||||
system.totalordermulticast.checkpoint_to_disk = false | |||||
system.totalordermulticast.sync_ckp = false | |||||
############################################ | |||||
###### Reconfiguration Configurations ###### | |||||
############################################ | |||||
#Replicas ID for the initial view, separated by a comma. | |||||
# The number of replicas in this parameter should be equal to that specified in 'system.servers.num' | |||||
system.initial.view = 0,1,2,3 | |||||
#The ID of the trust third party (TTP) | |||||
system.ttp.id = 7002 | |||||
#This sets if the system will function in Byzantine or crash-only mode. Set to "true" to support Byzantine faults | |||||
system.bft = true | |||||
#Custom View Storage; | |||||
#view.storage.handler=bftsmart.reconfiguration.views.DefaultViewStorage |
@@ -0,0 +1,208 @@ | |||||
# Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags | |||||
# | |||||
# Licensed under the Apache License, Version 2.0 (the "License"); | |||||
# you may not use this file except in compliance with the License. | |||||
# You may obtain a copy of the License at | |||||
# | |||||
# http://www.apache.org/licenses/LICENSE-2.0 | |||||
# | |||||
# Unless required by applicable law or agreed to in writing, software | |||||
# distributed under the License is distributed on an "AS IS" BASIS, | |||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
# See the License for the specific language governing permissions and | |||||
# limitations under the License. | |||||
############################################ | |||||
###### Consensus Commit Block Parameters: transaction count ###### | |||||
############################################ | |||||
system.block.txsize=500 | |||||
############################################ | |||||
###### Consensus Commit Block Parameters: delay time ###### | |||||
############################################ | |||||
system.block.maxdelay=5000 | |||||
############################################ | |||||
###### #Consensus Participant0 ###### | |||||
############################################ | |||||
system.server.0.pubkey=3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9 | |||||
system.server.0.network.host=127.0.0.1 | |||||
system.server.0.network.port=16000 | |||||
system.server.0.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant1 ###### | |||||
############################################ | |||||
system.server.1.pubkey=3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX | |||||
system.server.1.network.host=127.0.0.1 | |||||
system.server.1.network.port=16100 | |||||
system.server.1.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant2 ###### | |||||
############################################ | |||||
system.server.2.pubkey=3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x | |||||
system.server.2.network.host=127.0.0.1 | |||||
system.server.2.network.port=16200 | |||||
system.server.2.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant3 ###### | |||||
############################################ | |||||
system.server.3.pubkey=3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk | |||||
system.server.3.network.host=127.0.0.1 | |||||
system.server.3.network.port=16300 | |||||
system.server.3.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant4 ###### | |||||
############################################ | |||||
system.server.4.pubkey=3snPdw7i7PXPRMp3EAjsxJkHe7aZJRLNzdW8kEHBWeQsSgcPAiHP2J | |||||
system.server.4.network.host=127.0.0.1 | |||||
system.server.4.network.port=16400 | |||||
system.server.4.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant5 ###### | |||||
############################################ | |||||
system.server.5.pubkey=3snPdw7i7PmmQoPgUpUmmAUj6nakHj8wMSQmiMi1RaiZp4YU1D4AXk | |||||
system.server.5.network.host=127.0.0.1 | |||||
system.server.5.network.port=16500 | |||||
system.server.5.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant6 ###### | |||||
############################################ | |||||
system.server.6.pubkey=3snPdw7i7PiJKsa94q3EcLT1y6GRJ7LeFGe799hdzRRHmf6LNodyiM | |||||
system.server.6.network.host=127.0.0.1 | |||||
system.server.6.network.port=16600 | |||||
system.server.6.network.secure=false | |||||
############################################ | |||||
###### #Consensus Participant7 ###### | |||||
############################################ | |||||
system.server.7.pubkey=3snPdw7i7Pm2wJwEnKn8kK8eGTkN36C2BZRRjVTr9FPB2rqtcgTq7h | |||||
system.server.7.network.host=127.0.0.1 | |||||
system.server.7.network.port=16700 | |||||
system.server.7.network.secure=false | |||||
############################################ | |||||
####### Communication Configurations ####### | |||||
############################################ | |||||
#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) | |||||
#This parameter is not currently being used | |||||
#system.authentication.hmacAlgorithm = HmacSHA1 | |||||
#Specify if the communication system should use a thread to send data (true or false) | |||||
system.communication.useSenderThread = true | |||||
#Force all processes to use the same public/private keys pair and secret key. This is useful when deploying experiments | |||||
#and benchmarks, but must not be used in production systems. | |||||
system.communication.defaultkeys = true | |||||
############################################ | |||||
### Replication Algorithm Configurations ### | |||||
############################################ | |||||
#Number of servers in the group | |||||
system.servers.num = 8 | |||||
#Maximum number of faulty replicas | |||||
system.servers.f = 2 | |||||
#Timeout to asking for a client request | |||||
system.totalordermulticast.timeout = 2000 | |||||
#Maximum batch size (in number of messages) | |||||
system.totalordermulticast.maxbatchsize = 500 | |||||
#Number of nonces (for non-determinism actions) generated | |||||
system.totalordermulticast.nonces = 10 | |||||
#if verification of leader-generated timestamps are increasing | |||||
#it can only be used on systems in which the network clocks | |||||
#are synchronized | |||||
system.totalordermulticast.verifyTimestamps = false | |||||
#Quantity of messages that can be stored in the receive queue of the communication system | |||||
system.communication.inQueueSize = 500000 | |||||
# Quantity of messages that can be stored in the send queue of each replica | |||||
system.communication.outQueueSize = 500000 | |||||
#Set to 1 if SMaRt should use signatures, set to 0 if otherwise | |||||
system.communication.useSignatures = 0 | |||||
#Set to 1 if SMaRt should use MAC's, set to 0 if otherwise | |||||
system.communication.useMACs = 1 | |||||
#Set to 1 if SMaRt should use the standard output to display debug messages, set to 0 if otherwise | |||||
system.debug = 0 | |||||
#Print information about the replica when it is shutdown | |||||
system.shutdownhook = true | |||||
############################################ | |||||
###### State Transfer Configurations ####### | |||||
############################################ | |||||
#Activate the state transfer protocol ('true' to activate, 'false' to de-activate) | |||||
system.totalordermulticast.state_transfer = true | |||||
#Maximum ahead-of-time message not discarded | |||||
system.totalordermulticast.highMark = 10000 | |||||
#Maximum ahead-of-time message not discarded when the replica is still on EID 0 (after which the state transfer is triggered) | |||||
system.totalordermulticast.revival_highMark = 10 | |||||
#Number of ahead-of-time messages necessary to trigger the state transfer after a request timeout occurs | |||||
system.totalordermulticast.timeout_highMark = 200 | |||||
############################################ | |||||
###### Log and Checkpoint Configurations ### | |||||
############################################ | |||||
system.totalordermulticast.log = true | |||||
system.totalordermulticast.log_parallel = false | |||||
system.totalordermulticast.log_to_disk = false | |||||
system.totalordermulticast.sync_log = false | |||||
#Period at which BFT-SMaRt requests the state to the application (for the state transfer state protocol) | |||||
system.totalordermulticast.checkpoint_period = 1000 | |||||
system.totalordermulticast.global_checkpoint_period = 120000 | |||||
system.totalordermulticast.checkpoint_to_disk = false | |||||
system.totalordermulticast.sync_ckp = false | |||||
############################################ | |||||
###### Reconfiguration Configurations ###### | |||||
############################################ | |||||
#Replicas ID for the initial view, separated by a comma. | |||||
# The number of replicas in this parameter should be equal to that specified in 'system.servers.num' | |||||
system.initial.view = 0,1,2,3,4,5,6,7 | |||||
#The ID of the trust third party (TTP) | |||||
system.ttp.id = 7002 | |||||
#This sets if the system will function in Byzantine or crash-only mode. Set to "true" to support Byzantine faults | |||||
system.bft = true | |||||
#Custom View Storage; | |||||
#view.storage.handler=bftsmart.reconfiguration.views.DefaultViewStorage |
@@ -0,0 +1,72 @@ | |||||
#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; | |||||
ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffa | |||||
#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; | |||||
ledger.name=JDChain | |||||
#共识服务提供者;必须; | |||||
consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||||
#共识服务的参数配置;必须; | |||||
consensus.conf=classpath:bftsmart4.config | |||||
#密码服务提供者列表,以英文逗点“,”分隔;必须; | |||||
crypto.service-providers=com.jd.blockchain.crypto.service.classic.ClassicCryptoService, \ | |||||
com.jd.blockchain.crypto.service.sm.SMCryptoService | |||||
#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; | |||||
cons_parti.count=4 | |||||
#第0个参与方的名称; | |||||
cons_parti.0.name=jd.com | |||||
#第0个参与方的公钥文件路径; | |||||
cons_parti.0.pubkey-path= | |||||
#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
cons_parti.0.pubkey=3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9 | |||||
#第0个参与方的账本初始服务的主机; | |||||
cons_parti.0.initializer.host=127.0.0.1 | |||||
#第0个参与方的账本初始服务的端口; | |||||
cons_parti.0.initializer.port=1601 | |||||
#第0个参与方的账本初始服务是否开启安全连接; | |||||
cons_parti.0.initializer.secure=false | |||||
#第1个参与方的名称; | |||||
cons_parti.1.name=at.com | |||||
#第1个参与方的公钥文件路径; | |||||
cons_parti.1.pubkey-path= | |||||
#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
cons_parti.1.pubkey=3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX | |||||
#第1个参与方的账本初始服务的主机; | |||||
cons_parti.1.initializer.host=127.0.0.1 | |||||
#第1个参与方的账本初始服务的端口; | |||||
cons_parti.1.initializer.port=1611 | |||||
#第1个参与方的账本初始服务是否开启安全连接; | |||||
cons_parti.1.initializer.secure=false | |||||
#第2个参与方的名称; | |||||
cons_parti.2.name=bt.com | |||||
#第2个参与方的公钥文件路径; | |||||
cons_parti.2.pubkey-path= | |||||
#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
cons_parti.2.pubkey=3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x | |||||
#第2个参与方的账本初始服务的主机; | |||||
cons_parti.2.initializer.host=127.0.0.1 | |||||
#第2个参与方的账本初始服务的端口; | |||||
cons_parti.2.initializer.port=1621 | |||||
#第2个参与方的账本初始服务是否开启安全连接; | |||||
cons_parti.2.initializer.secure=false | |||||
#第3个参与方的名称; | |||||
cons_parti.3.name=xt.com | |||||
#第3个参与方的公钥文件路径; | |||||
cons_parti.3.pubkey-path= | |||||
#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
cons_parti.3.pubkey=3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk | |||||
#第3个参与方的账本初始服务的主机; | |||||
cons_parti.3.initializer.host=127.0.0.1 | |||||
#第3个参与方的账本初始服务的端口; | |||||
cons_parti.3.initializer.port=1631 | |||||
#第3个参与方的账本初始服务是否开启安全连接; | |||||
cons_parti.3.initializer.secure=false |
@@ -0,0 +1,125 @@ | |||||
#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; | |||||
ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe | |||||
#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; | |||||
ledger.name=JDChain | |||||
#共识服务提供者;必须; | |||||
consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||||
#共识服务的参数配置;必须; | |||||
consensus.conf=classpath:bftsmart8.config | |||||
#密码服务提供者列表,以英文逗点“,”分隔;必须; | |||||
crypto.service-providers=com.jd.blockchain.crypto.service.classic.ClassicCryptoService, \ | |||||
com.jd.blockchain.crypto.service.sm.SMCryptoService | |||||
#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; | |||||
cons_parti.count=8 | |||||
#第0个参与方的名称; | |||||
cons_parti.0.name=jd.com | |||||
#第0个参与方的公钥文件路径; | |||||
cons_parti.0.pubkey-path= | |||||
#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
cons_parti.0.pubkey=3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9 | |||||
#第0个参与方的账本初始服务的主机; | |||||
cons_parti.0.initializer.host=127.0.0.1 | |||||
#第0个参与方的账本初始服务的端口; | |||||
cons_parti.0.initializer.port=1601 | |||||
#第0个参与方的账本初始服务是否开启安全连接; | |||||
cons_parti.0.initializer.secure=false | |||||
#第1个参与方的名称; | |||||
cons_parti.1.name=at.com | |||||
#第1个参与方的公钥文件路径; | |||||
cons_parti.1.pubkey-path= | |||||
#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
cons_parti.1.pubkey=3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX | |||||
#第1个参与方的账本初始服务的主机; | |||||
cons_parti.1.initializer.host=127.0.0.1 | |||||
#第1个参与方的账本初始服务的端口; | |||||
cons_parti.1.initializer.port=1611 | |||||
#第1个参与方的账本初始服务是否开启安全连接; | |||||
cons_parti.1.initializer.secure=false | |||||
#第2个参与方的名称; | |||||
cons_parti.2.name=bt.com | |||||
#第2个参与方的公钥文件路径; | |||||
cons_parti.2.pubkey-path= | |||||
#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
cons_parti.2.pubkey=3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x | |||||
#第2个参与方的账本初始服务的主机; | |||||
cons_parti.2.initializer.host=127.0.0.1 | |||||
#第2个参与方的账本初始服务的端口; | |||||
cons_parti.2.initializer.port=1621 | |||||
#第2个参与方的账本初始服务是否开启安全连接; | |||||
cons_parti.2.initializer.secure=false | |||||
#第3个参与方的名称; | |||||
cons_parti.3.name=xt.com | |||||
#第3个参与方的公钥文件路径; | |||||
cons_parti.3.pubkey-path= | |||||
#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
cons_parti.3.pubkey=3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk | |||||
#第3个参与方的账本初始服务的主机; | |||||
cons_parti.3.initializer.host=127.0.0.1 | |||||
#第3个参与方的账本初始服务的端口; | |||||
cons_parti.3.initializer.port=1631 | |||||
#第3个参与方的账本初始服务是否开启安全连接; | |||||
cons_parti.3.initializer.secure=false | |||||
#第4个参与方的名称; | |||||
cons_parti.4.name=mt.com | |||||
#第4个参与方的公钥文件路径; | |||||
cons_parti.4.pubkey-path= | |||||
#第4个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
cons_parti.4.pubkey=3snPdw7i7PXPRMp3EAjsxJkHe7aZJRLNzdW8kEHBWeQsSgcPAiHP2J | |||||
#第4个参与方的账本初始服务的主机; | |||||
cons_parti.4.initializer.host=127.0.0.1 | |||||
#第4个参与方的账本初始服务的端口; | |||||
cons_parti.4.initializer.port=1641 | |||||
#第4个参与方的账本初始服务是否开启安全连接; | |||||
cons_parti.4.initializer.secure=false | |||||
#第5个参与方的名称; | |||||
cons_parti.5.name=nt.com | |||||
#第5个参与方的公钥文件路径; | |||||
cons_parti.5.pubkey-path= | |||||
#第5个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
cons_parti.5.pubkey=3snPdw7i7PmmQoPgUpUmmAUj6nakHj8wMSQmiMi1RaiZp4YU1D4AXk | |||||
#第5个参与方的账本初始服务的主机; | |||||
cons_parti.5.initializer.host=127.0.0.1 | |||||
#第5个参与方的账本初始服务的端口; | |||||
cons_parti.5.initializer.port=1651 | |||||
#第5个参与方的账本初始服务是否开启安全连接; | |||||
cons_parti.5.initializer.secure=false | |||||
#第6个参与方的名称; | |||||
cons_parti.6.name=yt.com | |||||
#第6个参与方的公钥文件路径; | |||||
cons_parti.6.pubkey-path= | |||||
#第6个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
cons_parti.6.pubkey=3snPdw7i7PiJKsa94q3EcLT1y6GRJ7LeFGe799hdzRRHmf6LNodyiM | |||||
#第6个参与方的账本初始服务的主机; | |||||
cons_parti.6.initializer.host=127.0.0.1 | |||||
#第6个参与方的账本初始服务的端口; | |||||
cons_parti.6.initializer.port=1661 | |||||
#第6个参与方的账本初始服务是否开启安全连接; | |||||
cons_parti.6.initializer.secure=false | |||||
#第7个参与方的名称; | |||||
cons_parti.7.name=zt.com | |||||
#第7个参与方的公钥文件路径; | |||||
cons_parti.7.pubkey-path= | |||||
#第7个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||||
cons_parti.7.pubkey=3snPdw7i7Pm2wJwEnKn8kK8eGTkN36C2BZRRjVTr9FPB2rqtcgTq7h | |||||
#第7个参与方的账本初始服务的主机; | |||||
cons_parti.7.initializer.host=127.0.0.1 | |||||
#第7个参与方的账本初始服务的端口; | |||||
cons_parti.7.initializer.port=1671 | |||||
#第7个参与方的账本初始服务是否开启安全连接; | |||||
cons_parti.7.initializer.secure=false |
@@ -0,0 +1,50 @@ | |||||
package test.com.jd.blockchain.contract; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.mocker.MockerNodeContext; | |||||
import com.jd.blockchain.mocker.contracts.AccountContract; | |||||
import com.jd.blockchain.mocker.contracts.AccountContractImpl; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
public class AccountMockerTest { | |||||
String accountFrom = "zhangsan"; | |||||
String accountTo = "lisi"; | |||||
MockerNodeContext mockerNodeContext = null; | |||||
HashDigest ledgerHash = null; | |||||
@Before | |||||
public void init() { | |||||
mockerNodeContext = new MockerNodeContext().build(); | |||||
ledgerHash = mockerNodeContext.getLedgerHash(); | |||||
} | |||||
@Test | |||||
public void test() { | |||||
// 首先创建一个数据账户 | |||||
String address = mockerNodeContext.registerDataAccount(); | |||||
// 处理合约 | |||||
AccountContract accountContract = new AccountContractImpl(); | |||||
// 发布合约 | |||||
accountContract = mockerNodeContext.deployContract(accountContract); | |||||
//首先创建账户 | |||||
accountContract.create(address, accountFrom, 1000L); | |||||
accountContract.create(address, accountTo, 1000L); | |||||
accountContract.print(address, accountFrom, accountTo); | |||||
// 开始转账 | |||||
accountContract.transfer(address, accountFrom, accountTo, 500); | |||||
// 打印转账后结果 | |||||
accountContract.print(address, accountFrom, accountTo); | |||||
} | |||||
} |
@@ -0,0 +1,54 @@ | |||||
package test.com.jd.blockchain.contract; | |||||
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.mocker.MockerNodeContext; | |||||
import com.jd.blockchain.mocker.config.MockerConstant; | |||||
import com.jd.blockchain.mocker.contracts.WriteContract; | |||||
import com.jd.blockchain.mocker.contracts.WriteContractImpl; | |||||
import org.junit.Test; | |||||
public class MockTest { | |||||
@Test | |||||
public void test() { | |||||
// 准备环境 | |||||
BlockchainKeypair blockchainKeypair = BlockchainKeyGenerator.getInstance().generate(); | |||||
MockerNodeContext mockerNodeContext = | |||||
new MockerNodeContext(MockerConstant.DEFAULT_LEDGER_SEED) | |||||
.participants("zhangsan", blockchainKeypair) | |||||
.build(); | |||||
HashDigest ledgerHash = mockerNodeContext.getLedgerHash(); | |||||
System.out.printf("LedgerHash = %s \r\n", ledgerHash.toBase58()); | |||||
System.out.printf("LedgerSeed = %s \r\n", mockerNodeContext.getLedgerSeed()); | |||||
// 注册用户 | |||||
String userAddress = mockerNodeContext.registerUser(BlockchainKeyGenerator.getInstance().generate()); | |||||
System.out.printf("----- 注册用户地址 {%s} -----\r\n", userAddress); | |||||
// 注册数据账户 | |||||
String dataAccountAddress = mockerNodeContext.registerDataAccount(BlockchainKeyGenerator.getInstance().generate()); | |||||
System.out.printf("----- 注册数据账户地址 {%s} -----\r\n", dataAccountAddress); | |||||
WriteContract writeContract = new WriteContractImpl(); | |||||
// 发布合约 | |||||
writeContract = mockerNodeContext.deployContract(writeContract); | |||||
writeContract.print("张三"); | |||||
String key = "Hello", value = "World"; | |||||
writeContract.writeKv(dataAccountAddress, key, value); | |||||
// 查询 | |||||
KVDataEntry[] kvDataEntries = mockerNodeContext.getDataEntries(ledgerHash, dataAccountAddress, key); | |||||
for (KVDataEntry kvDataEntry : kvDataEntries) { | |||||
System.out.printf("Key = %s, Value = %s \r\n", kvDataEntry.getKey(), kvDataEntry.getValue()); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,47 @@ | |||||
package test.com.jd.blockchain.contract; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | |||||
import com.jd.blockchain.mocker.MockerNodeContext; | |||||
import com.jd.blockchain.mocker.contracts.WriteContract; | |||||
import com.jd.blockchain.mocker.contracts.WriteContractImpl; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import static org.junit.Assert.assertEquals; | |||||
public class SampleTest { | |||||
MockerNodeContext mockerNodeContext = null; | |||||
HashDigest ledgerHash = null; | |||||
@Before | |||||
public void init() { | |||||
mockerNodeContext = new MockerNodeContext().build(); | |||||
ledgerHash = mockerNodeContext.getLedgerHash(); | |||||
} | |||||
@Test | |||||
public void writeTest() { | |||||
String key = "MyKey-" + System.currentTimeMillis(), value = "JDChain"; | |||||
WriteContract writeContract = new WriteContractImpl(); | |||||
String dataAccountAddress = mockerNodeContext.registerDataAccount(); | |||||
writeContract = mockerNodeContext.deployContract(writeContract); | |||||
writeContract.writeKv(dataAccountAddress, key, value); | |||||
// 查询结果 | |||||
KVDataEntry[] dataEntries = mockerNodeContext.getDataEntries(ledgerHash, dataAccountAddress, key); | |||||
for (KVDataEntry kvDataEntry : dataEntries) { | |||||
assertEquals(key, kvDataEntry.getKey()); | |||||
assertEquals(value, kvDataEntry.getValue()); | |||||
System.out.printf("Key = %s, Value = %s \r\n", kvDataEntry.getKey(), kvDataEntry.getValue()); | |||||
} | |||||
} | |||||
} |
@@ -22,7 +22,7 @@ public class BaseConstant { | |||||
public static final String SYS_CONTRACT_PROPS_NAME = "sys-contract.properties"; | public static final String SYS_CONTRACT_PROPS_NAME = "sys-contract.properties"; | ||||
public static final String CONTRACT_MAIN_CLASS_KEY = "contract"; | public static final String CONTRACT_MAIN_CLASS_KEY = "contract"; | ||||
public static final String CONTRACT_EVENT_PREFIX="@com.jd.blockchain.contract.model.ContractEvent(name="; | |||||
// public static final String CONTRACT_EVENT_PREFIX="@com.jd.blockchain.contract.ContractEvent(name="; | |||||
// 编译时引用包黑名单 | // 编译时引用包黑名单 | ||||
public static final String PACKAGE_BLACKLIST = "BLACKLIST"; | public static final String PACKAGE_BLACKLIST = "BLACKLIST"; | ||||
@@ -1,5 +1,7 @@ | |||||
package com.jd.blockchain.utils.security; | package com.jd.blockchain.utils.security; | ||||
import com.jd.blockchain.utils.io.BytesUtils; | |||||
import javax.crypto.Cipher; | import javax.crypto.Cipher; | ||||
import javax.crypto.KeyGenerator; | import javax.crypto.KeyGenerator; | ||||
import javax.crypto.SecretKey; | import javax.crypto.SecretKey; | ||||
@@ -32,7 +34,7 @@ public class DESUtils { | |||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); | SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); | ||||
SecretKey secretKey = keyFactory.generateSecret(keySpec); | SecretKey secretKey = keyFactory.generateSecret(keySpec); | ||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey); | cipher.init(Cipher.ENCRYPT_MODE, secretKey); | ||||
return cipher.doFinal(code.getBytes()); | |||||
return cipher.doFinal(BytesUtils.toBytes(code)); | |||||
} | } | ||||
/** DES解密 */ | /** DES解密 */ | ||||