diff --git a/.ci/ci_check.sh b/.ci/ci_check.sh index 1762bc3ea..5664a305e 100755 --- a/.ci/ci_check.sh +++ b/.ci/ci_check.sh @@ -5,7 +5,7 @@ set -e curl -LO https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/dev/tools/build_chain.sh && chmod u+x build_chain.sh bash <(curl -s https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/dev/tools/ci/download_bin.sh) -b dev -m echo "127.0.0.1:4 agency1 1,2,3" > ipconf -./build_chain.sh -e bin/fisco-bcos -f ipconf -p 30300,20200,8545 -v 2.0.0 +./build_chain.sh -e bin/fisco-bcos -f ipconf -p 30300,20200,8545 -v 2.1.0 ./nodes/127.0.0.1/start_all.sh ./nodes/127.0.0.1/fisco-bcos -v cp nodes/127.0.0.1/sdk/* src/integration-test/resources/ @@ -13,4 +13,4 @@ mv src/integration-test/resources/applicationContext-sample.xml src/integration- ./gradlew verifyGoogleJavaFormat ./gradlew build ./gradlew test -./gradlew integrationTest \ No newline at end of file +./gradlew integrationTest diff --git a/Changelog.md b/Changelog.md index e399c33eb..ebe3d7d67 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,20 @@ +### v2.1.1 + +(2019-10-29) + +* 增加 +1. 生成`java`合约代码添加`input`和`output`的解析接口 + +* 更新 +1. 修复`SDK`与节点握手协议的漏洞 +2. 部署合约`SDK`时不再通过轮询方式判断是否成功 +3. 修复`TransactionReceipt`类的`isStatusOK`接口抛出异常的问题 + +* 兼容 + +1. 兼容Channel Message v1协议 +2. 兼容FISCO BCOS 2.1以下的sdk证书名node.crt和node.key + ### v2.1.0 (2019-09-17) diff --git a/release_note.txt b/release_note.txt index 1defe531b..826e14246 100644 --- a/release_note.txt +++ b/release_note.txt @@ -1 +1 @@ -v2.1.0 +v2.1.1 diff --git a/src/main/java/org/fisco/bcos/channel/event/filter/EventLogFilterManager.java b/src/main/java/org/fisco/bcos/channel/event/filter/EventLogFilterManager.java index 24d07d94d..de2c7e2dd 100644 --- a/src/main/java/org/fisco/bcos/channel/event/filter/EventLogFilterManager.java +++ b/src/main/java/org/fisco/bcos/channel/event/filter/EventLogFilterManager.java @@ -151,7 +151,7 @@ public void run() { Thread.currentThread().interrupt(); } - logger.info( + logger.debug( " event filter manager, filter: {}, callback: {}", registerIDToFilter.size(), filterIDToCallback.size()); diff --git a/src/main/java/org/fisco/bcos/channel/event/filter/EventLogUserParams.java b/src/main/java/org/fisco/bcos/channel/event/filter/EventLogUserParams.java index 7117cccbc..497ba15b8 100644 --- a/src/main/java/org/fisco/bcos/channel/event/filter/EventLogUserParams.java +++ b/src/main/java/org/fisco/bcos/channel/event/filter/EventLogUserParams.java @@ -100,6 +100,9 @@ private boolean validTopics() { } for (Object topic : getTopics()) { + if (topic == null) { + continue; + } if (topic instanceof String) { // if valid topic if (((String) topic).isEmpty()) { diff --git a/src/main/java/org/fisco/bcos/channel/event/filter/TopicTools.java b/src/main/java/org/fisco/bcos/channel/event/filter/TopicTools.java index 99e1879d7..02326e4eb 100644 --- a/src/main/java/org/fisco/bcos/channel/event/filter/TopicTools.java +++ b/src/main/java/org/fisco/bcos/channel/event/filter/TopicTools.java @@ -1,7 +1,10 @@ package org.fisco.bcos.channel.event.filter; import java.math.BigInteger; +import org.fisco.bcos.web3j.abi.TypeEncoder; +import org.fisco.bcos.web3j.abi.datatypes.Bytes; import org.fisco.bcos.web3j.crypto.Hash; +import org.fisco.bcos.web3j.crypto.WalletUtils; import org.fisco.bcos.web3j.utils.Numeric; public class TopicTools { @@ -20,7 +23,12 @@ public static String boolToTopic(boolean b) { } public static String addressToTopic(String s) { - return s; + + if (!WalletUtils.isValidAddress(s)) { + throw new IllegalArgumentException("invalid address"); + } + + return "0x000000000000000000000000" + Numeric.cleanHexPrefix(s); } public static String stringToTopic(String s) { @@ -34,6 +42,12 @@ public static String bytesToTopic(byte[] b) { } public static String byteNToTopic(byte[] b) { - return Numeric.toHexString(b); + // byte[] can't be more than 32 byte + if (b.length > 32) { + throw new IllegalArgumentException("byteN can't be more than 32 byte"); + } + + Bytes bs = new Bytes(b.length, b); + return Numeric.prependHexPrefix(TypeEncoder.encode(bs)); } } diff --git a/src/main/java/org/fisco/bcos/fisco/EnumNodeVersion.java b/src/main/java/org/fisco/bcos/fisco/EnumNodeVersion.java index 68bbd045a..0f098da88 100644 --- a/src/main/java/org/fisco/bcos/fisco/EnumNodeVersion.java +++ b/src/main/java/org/fisco/bcos/fisco/EnumNodeVersion.java @@ -1,5 +1,7 @@ package org.fisco.bcos.fisco; +import org.fisco.bcos.channel.protocol.ChannelPrococolExceiption; + public enum EnumNodeVersion { BCOS_2_0_0_RC1("2.0.0-rc1"), BCOS_2_0_0_RC2("2.0.0-rc2"), @@ -22,11 +24,91 @@ public void setVersion(String version) { this.version = version; } - public static boolean channelProtocolHandleShakeSupport(String version) { - return !(version.equals(BCOS_2_0_0_RC1.getVersion()) - || version.equals(BCOS_2_0_0_RC2.getVersion()) - || version.equals(BCOS_2_0_0_RC3.getVersion()) - || version.equals(BCOS_2_0_0.getVersion()) - || version.equals(BCOS_2_0_1.getVersion())); + // the object of node version + class Version { + private int major; + private int minor; + private int patch; + private String ext; + + @Override + public String toString() { + return "Version [major=" + + major + + ", minor=" + + minor + + ", patch=" + + patch + + ", ext=" + + ext + + "]"; + } + + public int getMajor() { + return major; + } + + public void setMajor(int major) { + this.major = major; + } + + public int getMinor() { + return minor; + } + + public void setMinor(int minor) { + this.minor = minor; + } + + public int getPatch() { + return patch; + } + + public void setPatch(int patch) { + this.patch = patch; + } + + public String getExt() { + return ext; + } + + public void setExt(String ext) { + this.ext = ext; + } + } + + private static Version getClassVersion(String version) throws ChannelPrococolExceiption { + try { + // node version str format : "a.b.c" or "a.b.c-rcx" + String[] s0 = version.trim().split("-"); + + Version v = EnumNodeVersion.BCOS_2_0_0.new Version(); + if (s0.length > 1) { + v.setExt(s0[1]); + } + + // + String[] s1 = s0[0].split("\\."); + if (s1.length >= 3) { + v.setMajor(Integer.parseInt(s1[0].trim())); + v.setMinor(Integer.parseInt(s1[1].trim())); + v.setPatch(Integer.parseInt(s1[2].trim())); + } else { // invaid format + throw new ChannelPrococolExceiption( + " invalid node version format, version: " + version); + } + + return v; + } catch (Exception e) { + throw new ChannelPrococolExceiption( + " invalid node version format, version: " + version); + } + } + + public static boolean channelProtocolHandleShakeSupport(String version) + throws ChannelPrococolExceiption { + Version v = getClassVersion(version); + // 2.1.0 and above + return (v.getMajor() == 2) && (v.getMinor() >= 1); } } diff --git a/src/main/java/org/fisco/bcos/web3j/abi/datatypes/Bytes.java b/src/main/java/org/fisco/bcos/web3j/abi/datatypes/Bytes.java index ffd2680d8..f2201e6ff 100644 --- a/src/main/java/org/fisco/bcos/web3j/abi/datatypes/Bytes.java +++ b/src/main/java/org/fisco/bcos/web3j/abi/datatypes/Bytes.java @@ -5,7 +5,7 @@ public class Bytes extends BytesType { public static final String TYPE_NAME = "bytes"; - protected Bytes(int byteSize, byte[] value) { + public Bytes(int byteSize, byte[] value) { super(value, TYPE_NAME + value.length); if (!isValid(byteSize, value)) { throw new UnsupportedOperationException( diff --git a/src/main/java/org/fisco/bcos/web3j/codegen/SolidityFunctionWrapper.java b/src/main/java/org/fisco/bcos/web3j/codegen/SolidityFunctionWrapper.java index 617b03f29..6f837e18a 100644 --- a/src/main/java/org/fisco/bcos/web3j/codegen/SolidityFunctionWrapper.java +++ b/src/main/java/org/fisco/bcos/web3j/codegen/SolidityFunctionWrapper.java @@ -11,13 +11,11 @@ import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; -import io.reactivex.Flowable; import java.io.IOException; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -30,6 +28,7 @@ import javax.lang.model.element.Modifier; import org.fisco.bcos.web3j.abi.EventEncoder; import org.fisco.bcos.web3j.abi.FunctionEncoder; +import org.fisco.bcos.web3j.abi.FunctionReturnDecoder; import org.fisco.bcos.web3j.abi.TypeReference; import org.fisco.bcos.web3j.abi.datatypes.Address; import org.fisco.bcos.web3j.abi.datatypes.Bool; @@ -45,13 +44,14 @@ import org.fisco.bcos.web3j.protocol.ObjectMapperFactory; import org.fisco.bcos.web3j.protocol.Web3j; import org.fisco.bcos.web3j.protocol.core.RemoteCall; -import org.fisco.bcos.web3j.protocol.core.methods.request.BcosFilter; import org.fisco.bcos.web3j.protocol.core.methods.response.AbiDefinition; +import org.fisco.bcos.web3j.protocol.core.methods.response.AbiDefinition.NamedType; import org.fisco.bcos.web3j.protocol.core.methods.response.Log; import org.fisco.bcos.web3j.protocol.core.methods.response.TransactionReceipt; import org.fisco.bcos.web3j.tx.Contract; import org.fisco.bcos.web3j.tx.TransactionManager; import org.fisco.bcos.web3j.tx.gas.ContractGasProvider; +import org.fisco.bcos.web3j.tx.txdecode.TransactionDecoder; import org.fisco.bcos.web3j.utils.Collection; import org.fisco.bcos.web3j.utils.Strings; import org.fisco.bcos.web3j.utils.Version; @@ -68,11 +68,11 @@ public class SolidityFunctionWrapper extends Generator { private static final String CREDENTIALS = "credentials"; private static final String CONTRACT_GAS_PROVIDER = "contractGasProvider"; private static final String TRANSACTION_MANAGER = "transactionManager"; + private static final String TRANSACTION_DECODER = "transactionDecoder"; private static final String INITIAL_VALUE = "initialWeiValue"; private static final String CONTRACT_ADDRESS = "contractAddress"; private static final String GAS_PRICE = "gasPrice"; private static final String GAS_LIMIT = "gasLimit"; - private static final String FILTER = "filter"; private static final String FROM_BLOCK = "fromBlock"; private static final String TO_BLOCK = "toBlock"; private static final String WEI_VALUE = "weiValue"; @@ -107,7 +107,6 @@ public SolidityFunctionWrapper(boolean useNativeJavaTypes) { this.reporter = reporter; } - @SuppressWarnings("unchecked") public void generateJavaFiles( String contractName, String bin, @@ -143,6 +142,7 @@ void generateJavaFiles( TypeSpec.Builder classBuilder = createClassBuilder(className, bin, abi); + classBuilder.addMethod(buildReturnTransactionDecoder()); classBuilder.addMethod(buildConstructor(Credentials.class, CREDENTIALS, false)); classBuilder.addMethod(buildConstructor(Credentials.class, CREDENTIALS, true)); classBuilder.addMethod( @@ -161,63 +161,9 @@ void generateJavaFiles( classBuilder.addMethods(buildDeployMethods(className, classBuilder, abi)); } - addAddressesSupport(classBuilder, addresses); - write(basePackageName, classBuilder.build(), destinationDir); } - private void addAddressesSupport(TypeSpec.Builder classBuilder, Map addresses) { - if (addresses != null) { - - ClassName stringType = ClassName.get(String.class); - ClassName mapType = ClassName.get(HashMap.class); - TypeName mapStringString = ParameterizedTypeName.get(mapType, stringType, stringType); - FieldSpec addressesStaticField = - FieldSpec.builder( - mapStringString, - "_addresses", - Modifier.PROTECTED, - Modifier.STATIC, - Modifier.FINAL) - .build(); - classBuilder.addField(addressesStaticField); - - final CodeBlock.Builder staticInit = CodeBlock.builder(); - staticInit.addStatement("_addresses = new HashMap()"); - addresses.forEach( - (k, v) -> - staticInit.addStatement( - String.format("_addresses.put(\"%1s\", \"%2s\")", k, v))); - classBuilder.addStaticBlock(staticInit.build()); - - // See Contract#getStaticDeployedAddress(String) - MethodSpec getAddress = - MethodSpec.methodBuilder("getStaticDeployedAddress") - .addModifiers(Modifier.PROTECTED) - .returns(stringType) - .addParameter(stringType, "networkId") - .addCode( - CodeBlock.builder() - .addStatement("return _addresses.get(networkId)") - .build()) - .build(); - classBuilder.addMethod(getAddress); - - MethodSpec getPreviousAddress = - MethodSpec.methodBuilder("getPreviouslyDeployedAddress") - .addModifiers(Modifier.PUBLIC) - .addModifiers(Modifier.STATIC) - .returns(stringType) - .addParameter(stringType, "networkId") - .addCode( - CodeBlock.builder() - .addStatement("return _addresses.get(networkId)") - .build()) - .build(); - classBuilder.addMethod(getPreviousAddress); - } - } - private TypeSpec.Builder createClassBuilder( String className, String binary, List abi) { @@ -232,7 +178,8 @@ private TypeSpec.Builder createClassBuilder( .addMember("value", "$S", "unchecked") .build()) .addField(createBinaryDefinition(binary)) - .addField(createABIDefinition(abi)); + .addField(createABIDefinition(abi)) + .addField(createTransactionDecoderDefinition()); } private String getWeb3jVersion() { @@ -248,6 +195,14 @@ private String getWeb3jVersion() { return "\n

Generated with web3j version " + version + ".\n"; } + private FieldSpec createTransactionDecoderDefinition() { + + return FieldSpec.builder(TransactionDecoder.class, TRANSACTION_DECODER) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("new $T($N, $N)", TransactionDecoder.class, "ABI", "BINARY") + .build(); + } + private FieldSpec createBinaryDefinition(String binary) { return FieldSpec.builder(String.class, BINARY) @@ -278,6 +233,22 @@ private String buildEventDefinitionName(String eventName) { return eventName.toUpperCase() + "_EVENT"; } + private static boolean isOverLoadFunction( + String name, List functionDefinitions) { + int count = 0; + for (AbiDefinition functionDefinition : functionDefinitions) { + if (!functionDefinition.getType().equals("function")) { + continue; + } + + if (functionDefinition.getName().equals(name)) { + count += 1; + } + } + + return count > 1; + } + private List buildFunctionDefinitions( String className, TypeSpec.Builder classBuilder, @@ -296,6 +267,20 @@ private List buildFunctionDefinitions( MethodSpec msSeq = buildFunctionSeq(functionDefinition); methodSpecs.add(msSeq); + + boolean isOverLoad = + isOverLoadFunction(functionDefinition.getName(), functionDefinitions); + if (!functionDefinition.getInputs().isEmpty()) { + MethodSpec inputDecoder = + buildFunctionWithInputDecoder(functionDefinition, isOverLoad); + methodSpecs.add(inputDecoder); + } + + if (!functionDefinition.getOutputs().isEmpty()) { + MethodSpec outputDecoder = + buildFunctionWithOutputDecoder(functionDefinition, isOverLoad); + methodSpecs.add(outputDecoder); + } } } else if (functionDefinition.getType().equals("event")) { methodSpecs.addAll(buildEventFunctions(functionDefinition, classBuilder)); @@ -416,6 +401,18 @@ Iterable buildFuncNameConstants(List functionDefinitio return fields; } + private static MethodSpec buildReturnTransactionDecoder() { + + MethodSpec.Builder toReturn = + MethodSpec.methodBuilder("getTransactionDecoder") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(TransactionDecoder.class); + + toReturn.addStatement("return $N", TRANSACTION_DECODER); + + return toReturn.build(); + } + private static MethodSpec buildConstructor( Class authType, String authName, boolean withGasProvider) { MethodSpec.Builder toReturn = @@ -684,6 +681,28 @@ private String addParameters( } } + private String addParameters(List namedTypes) { + + List inputParameterTypes = buildParameterTypes(namedTypes); + + List nativeInputParameterTypes = new ArrayList<>(inputParameterTypes.size()); + for (ParameterSpec parameterSpec : inputParameterTypes) { + TypeName typeName = getWrapperType(parameterSpec.type); + nativeInputParameterTypes.add( + ParameterSpec.builder(typeName, parameterSpec.name).build()); + } + + if (useNativeJavaTypes) { + return Collection.join( + namedTypes, + ", \n", + // this results in fully qualified names being generated + this::createMappedParameterTypes); + } else { + return Collection.join(inputParameterTypes, ", ", parameterSpec -> parameterSpec.name); + } + } + private String createMappedParameterTypes(AbiDefinition.NamedType namedType) { String name = namedType.getName(); @@ -941,6 +960,118 @@ MethodSpec buildFunctionWithCallback(AbiDefinition functionDefinition) return methodBuilder.build(); } + public static String getInputOutputFunctionName( + AbiDefinition functionDefinition, boolean input, boolean isOverLoad) { + if (!isOverLoad) { + return functionDefinition.getName(); + } + + List nameTypes = + (input ? functionDefinition.getInputs() : functionDefinition.getOutputs()); + + String name = functionDefinition.getName(); + for (int i = 0; i < nameTypes.size(); i++) { + AbiDefinition.NamedType.Type type = + new AbiDefinition.NamedType.Type(nameTypes.get(i).getType()); + List depths = type.getDepthArray(); + name += Strings.capitaliseFirstLetter(type.getBaseName()); + for (int j = 0; j < depths.size(); j++) { + name += "Array"; + if (0 != depths.get(j)) { + name += String.valueOf(depths.get(j)); + } + } + } + + return name; + } + + private MethodSpec buildFunctionWithInputDecoder( + AbiDefinition functionDefinition, boolean isOverLoad) throws ClassNotFoundException { + + String functionName = getInputOutputFunctionName(functionDefinition, true, isOverLoad); + + MethodSpec.Builder methodBuilder = + MethodSpec.methodBuilder( + "get" + Strings.capitaliseFirstLetter(functionName) + "Input") + .addModifiers(Modifier.PUBLIC) + .addParameter(TransactionReceipt.class, "transactionReceipt"); + + List returnTypes = + buildReturnTypes(buildTypeNames(functionDefinition.getInputs())); + + ParameterizedTypeName parameterizedTupleType = + ParameterizedTypeName.get( + ClassName.get( + "org.fisco.bcos.web3j.tuples.generated", + "Tuple" + returnTypes.size()), + returnTypes.toArray(new TypeName[returnTypes.size()])); + + methodBuilder.returns(parameterizedTupleType); + methodBuilder.addStatement("String data = transactionReceipt.getInput().substring(10)"); + + buildVariableLengthReturnFunctionConstructor( + methodBuilder, + functionDefinition.getName(), + "", + buildTypeNames(functionDefinition.getInputs())); + + methodBuilder.addStatement( + "$T results = $T.decode(data, function.getOutputParameters());", + List.class, + FunctionReturnDecoder.class); + + buildTupleResultContainer0( + methodBuilder, + parameterizedTupleType, + buildTypeNames(functionDefinition.getInputs())); + + return methodBuilder.build(); + } + + private MethodSpec buildFunctionWithOutputDecoder( + AbiDefinition functionDefinition, boolean isOverLoad) throws ClassNotFoundException { + + String functionName = getInputOutputFunctionName(functionDefinition, false, isOverLoad); + + MethodSpec.Builder methodBuilder = + MethodSpec.methodBuilder( + "get" + Strings.capitaliseFirstLetter(functionName) + "Output") + .addModifiers(Modifier.PUBLIC) + .addParameter(TransactionReceipt.class, "transactionReceipt"); + + List returnTypes = + buildReturnTypes(buildTypeNames(functionDefinition.getOutputs())); + + ParameterizedTypeName parameterizedTupleType = + ParameterizedTypeName.get( + ClassName.get( + "org.fisco.bcos.web3j.tuples.generated", + "Tuple" + returnTypes.size()), + returnTypes.toArray(new TypeName[returnTypes.size()])); + + methodBuilder.returns(parameterizedTupleType); + methodBuilder.addStatement("String data = transactionReceipt.getOutput()"); + + buildVariableLengthReturnFunctionConstructor( + methodBuilder, + functionDefinition.getName(), + "", + buildTypeNames(functionDefinition.getOutputs())); + + methodBuilder.addStatement( + "$T results = $T.decode(data, function.getOutputParameters());", + List.class, + FunctionReturnDecoder.class); + + buildTupleResultContainer0( + methodBuilder, + parameterizedTupleType, + buildTypeNames(functionDefinition.getOutputs())); + + return methodBuilder.build(); + } + private void buildConstantFunction( AbiDefinition functionDefinition, MethodSpec.Builder methodBuilder, @@ -1192,90 +1323,6 @@ TypeSpec buildEventResponseObject( return builder.build(); } - MethodSpec buildEventFlowableFunction( - String responseClassName, - String functionName, - List indexedParameters, - List nonIndexedParameters) - throws ClassNotFoundException { - - String generatedFunctionName = Strings.lowercaseFirstLetter(functionName) + "EventFlowable"; - ParameterizedTypeName parameterizedTypeName = - ParameterizedTypeName.get( - ClassName.get(Flowable.class), ClassName.get("", responseClassName)); - - MethodSpec.Builder flowableMethodBuilder = - MethodSpec.methodBuilder(generatedFunctionName) - .addModifiers(Modifier.PUBLIC) - .addParameter(BcosFilter.class, FILTER) - .returns(parameterizedTypeName); - - TypeSpec converter = - TypeSpec.anonymousClassBuilder("") - .addSuperinterface( - ParameterizedTypeName.get( - ClassName.get(io.reactivex.functions.Function.class), - ClassName.get(Log.class), - ClassName.get("", responseClassName))) - .addMethod( - MethodSpec.methodBuilder("apply") - .addAnnotation(Override.class) - .addModifiers(Modifier.PUBLIC) - .addParameter(Log.class, "log") - .returns(ClassName.get("", responseClassName)) - .addStatement( - "$T eventValues = extractEventParametersWithLog(" - + buildEventDefinitionName(functionName) - + ", log)", - Contract.EventValuesWithLog.class) - .addStatement( - "$1T typedResponse = new $1T()", - ClassName.get("", responseClassName)) - .addCode( - buildTypedResponse( - "typedResponse", - indexedParameters, - nonIndexedParameters, - true)) - .addStatement("return typedResponse") - .build()) - .build(); - - flowableMethodBuilder.addStatement("return web3j.logFlowable(filter).map($L)", converter); - - return flowableMethodBuilder.build(); - } - - MethodSpec buildDefaultEventFlowableFunction(String responseClassName, String functionName) { - - String generatedFunctionName = Strings.lowercaseFirstLetter(functionName) + "EventFlowable"; - ParameterizedTypeName parameterizedTypeName = - ParameterizedTypeName.get( - ClassName.get(Flowable.class), ClassName.get("", responseClassName)); - - MethodSpec.Builder flowableMethodBuilder = - MethodSpec.methodBuilder(generatedFunctionName) - .addModifiers(Modifier.PUBLIC) - .addParameter(String.class, FROM_BLOCK) - .addParameter(String.class, TO_BLOCK) - .returns(parameterizedTypeName); - - flowableMethodBuilder - .addStatement( - "$1T filter = new $1T($2L, $3L, " + "getContractAddress())", - BcosFilter.class, - FROM_BLOCK, - TO_BLOCK) - .addStatement( - "filter.addSingleTopic($T.encode(" - + buildEventDefinitionName(functionName) - + "))", - EventEncoder.class) - .addStatement("return " + generatedFunctionName + "(filter)"); - - return flowableMethodBuilder.build(); - } - private MethodSpec buildRegisterEventLogPushFunction(String eventName) throws ClassNotFoundException { @@ -1413,11 +1460,6 @@ List buildEventFunctions( methods.add(buildRegisterEventLogPushFunction(functionName)); methods.add(buildDefaultRegisterEventLogPushFunction(functionName)); - // methods.add( - // buildEventFlowableFunction( - // responseClassName, functionName, indexedParameters, - // nonIndexedParameters)); - // methods.add(buildDefaultEventFlowableFunction(responseClassName, functionName)); return methods; } @@ -1619,6 +1661,52 @@ private void buildTupleResultContainer( methodBuilder.addStatement("return new $T(\n$L)", buildRemoteCall(tupleType), callableType); } + private void buildTupleResultContainer0( + MethodSpec.Builder methodBuilder, + ParameterizedTypeName tupleType, + List outputParameterTypes) + throws ClassNotFoundException { + + List typeArguments = tupleType.typeArguments; + + CodeBlock.Builder codeBuilder = CodeBlock.builder(); + + String resultStringSimple = "\n($T) results.get($L)"; + if (useNativeJavaTypes) { + resultStringSimple += ".getValue()"; + } + + String resultStringNativeList = "\nconvertToNative(($T) results.get($L).getValue())"; + + int size = typeArguments.size(); + ClassName classList = ClassName.get(List.class); + + for (int i = 0; i < size; i++) { + TypeName param = outputParameterTypes.get(i); + TypeName convertTo = typeArguments.get(i); + + String resultString = resultStringSimple; + + // If we use native java types we need to convert + // elements of arrays to native java types too + if (useNativeJavaTypes && param instanceof ParameterizedTypeName) { + ParameterizedTypeName oldContainer = (ParameterizedTypeName) param; + ParameterizedTypeName newContainer = (ParameterizedTypeName) convertTo; + if (newContainer.rawType.compareTo(classList) == 0 + && newContainer.typeArguments.size() == 1) { + convertTo = + ParameterizedTypeName.get(classList, oldContainer.typeArguments.get(0)); + resultString = resultStringNativeList; + } + } + + codeBuilder.add(resultString, convertTo, i); + codeBuilder.add(i < size - 1 ? ", " : "\n"); + } + + methodBuilder.addStatement("return new $T(\n$L)", tupleType, codeBuilder.build()); + } + private static CodeBlock buildVariableLengthEventInitializer( String eventName, List parameterTypes) { @@ -1653,7 +1741,7 @@ private static CodeBlock buildVariableLengthEventInitializer( .build(); } - private List loadContractDefinition(String abi) throws IOException { + public static List loadContractDefinition(String abi) throws IOException { ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper(); AbiDefinition[] abiDefinition = objectMapper.readValue(abi, AbiDefinition[].class); return Arrays.asList(abiDefinition); diff --git a/src/main/java/org/fisco/bcos/web3j/protocol/channel/StatusCode.java b/src/main/java/org/fisco/bcos/web3j/protocol/channel/StatusCode.java index 0cbc113b9..69893f00f 100644 --- a/src/main/java/org/fisco/bcos/web3j/protocol/channel/StatusCode.java +++ b/src/main/java/org/fisco/bcos/web3j/protocol/channel/StatusCode.java @@ -29,6 +29,9 @@ public class StatusCode { public static final String AddressAlreadyUsed = "0x18"; public static final String PermissionDenied = "0x19"; public static final String CallAddressError = "0x1a"; + public static final String GasOverflow = "0x1b"; + public static final String TxPoolIsFull = "0x1c"; + public static final String TransactionRefused = "0x1d"; public static String getStatusMessage(String status) { return getStatusMessage(status, " Error code: " + status); @@ -50,7 +53,7 @@ public static String getStatusMessage(String status, String errorMessage) { message = "invalid format"; break; case OutOfGasIntrinsic: - message = "out of gas intrinsic"; + message = "out of gas"; break; case InvalidSignature: message = "invalid signature"; @@ -118,6 +121,15 @@ public static String getStatusMessage(String status, String errorMessage) { case CallAddressError: message = "call address error"; break; + case GasOverflow: + message = "gas over flow"; + break; + case TxPoolIsFull: + message = "transaction pool is full"; + break; + case TransactionRefused: + message = "transaction refuesd"; + break; default: message = errorMessage; break; diff --git a/src/main/java/org/fisco/bcos/web3j/protocol/core/methods/response/AbiDefinition.java b/src/main/java/org/fisco/bcos/web3j/protocol/core/methods/response/AbiDefinition.java index a562c0faa..c53150502 100644 --- a/src/main/java/org/fisco/bcos/web3j/protocol/core/methods/response/AbiDefinition.java +++ b/src/main/java/org/fisco/bcos/web3j/protocol/core/methods/response/AbiDefinition.java @@ -306,6 +306,10 @@ public boolean dynamicArray() { public int getDepth() { return depth.size(); } + + public List getDepthArray() { + return depth; + } } } } diff --git a/src/main/java/org/fisco/bcos/web3j/protocol/core/methods/response/TransactionReceipt.java b/src/main/java/org/fisco/bcos/web3j/protocol/core/methods/response/TransactionReceipt.java index 8b21680de..26041173a 100644 --- a/src/main/java/org/fisco/bcos/web3j/protocol/core/methods/response/TransactionReceipt.java +++ b/src/main/java/org/fisco/bcos/web3j/protocol/core/methods/response/TransactionReceipt.java @@ -163,8 +163,13 @@ public boolean isStatusOK() { if (null == status) { return true; } - BigInteger statusQuantity = Numeric.decodeQuantity(status); - return BigInteger.ZERO.equals(statusQuantity); + + try { + BigInteger statusQuantity = Numeric.decodeQuantity(status); + return BigInteger.ZERO.equals(statusQuantity); + } catch (Exception e) { + return false; + } } public String getFrom() { diff --git a/src/main/java/org/fisco/bcos/web3j/tx/Contract.java b/src/main/java/org/fisco/bcos/web3j/tx/Contract.java index e358c2bfa..2153f948b 100644 --- a/src/main/java/org/fisco/bcos/web3j/tx/Contract.java +++ b/src/main/java/org/fisco/bcos/web3j/tx/Contract.java @@ -3,7 +3,11 @@ import java.io.IOException; import java.lang.reflect.Constructor; import java.math.BigInteger; -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.Semaphore; import java.util.stream.Collectors; import org.fisco.bcos.channel.client.TransactionSucCallback; @@ -23,16 +27,20 @@ import org.fisco.bcos.web3j.protocol.Web3j; import org.fisco.bcos.web3j.protocol.Web3jService; import org.fisco.bcos.web3j.protocol.channel.ChannelEthereumService; +import org.fisco.bcos.web3j.protocol.channel.StatusCode; import org.fisco.bcos.web3j.protocol.core.DefaultBlockParameter; import org.fisco.bcos.web3j.protocol.core.DefaultBlockParameterName; import org.fisco.bcos.web3j.protocol.core.JsonRpc2_0Web3j; import org.fisco.bcos.web3j.protocol.core.RemoteCall; import org.fisco.bcos.web3j.protocol.core.methods.request.Transaction; -import org.fisco.bcos.web3j.protocol.core.methods.response.*; +import org.fisco.bcos.web3j.protocol.core.methods.response.Call; +import org.fisco.bcos.web3j.protocol.core.methods.response.Code; +import org.fisco.bcos.web3j.protocol.core.methods.response.Log; +import org.fisco.bcos.web3j.protocol.core.methods.response.NodeVersion; +import org.fisco.bcos.web3j.protocol.core.methods.response.TransactionReceipt; import org.fisco.bcos.web3j.protocol.exceptions.TransactionException; import org.fisco.bcos.web3j.tx.exceptions.ContractCallException; import org.fisco.bcos.web3j.tx.gas.ContractGasProvider; -import org.fisco.bcos.web3j.tx.gas.DefaultGasProvider; import org.fisco.bcos.web3j.tx.gas.StaticGasProvider; import org.fisco.bcos.web3j.tx.txdecode.TransactionDecoder; import org.fisco.bcos.web3j.utils.Numeric; @@ -44,11 +52,7 @@ */ public abstract class Contract extends ManagedTransaction { - /** - * @see DefaultGasProvider - * @deprecated ... - */ - static Logger logger = LoggerFactory.getLogger(Contract.class); + private static final Logger logger = LoggerFactory.getLogger(Contract.class); public static final BigInteger GAS_LIMIT = BigInteger.valueOf(4_300_000); @@ -59,7 +63,6 @@ public abstract class Contract extends ManagedTransaction { protected String contractAddress; protected ContractGasProvider gasProvider; protected TransactionReceipt transactionReceipt; - protected Map deployedAddresses; protected DefaultBlockParameter defaultBlockParameter = DefaultBlockParameterName.LATEST; protected Contract( @@ -334,31 +337,31 @@ protected List executeCallMultipleValueReturn(Function function) throws IO return executeCall(function); } - protected TransactionReceipt executeTransaction(Function function) - throws IOException, TransactionException { - - class Callback extends TransactionSucCallback { - Callback() { - try { - semaphore.acquire(1); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } + class Callback extends TransactionSucCallback { + Callback() { + try { + semaphore.acquire(1); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } + } - @Override - public void onResponse(TransactionReceipt receipt) { - this.receipt = receipt; - semaphore.release(); - } + @Override + public void onResponse(TransactionReceipt receipt) { + this.receipt = receipt; + semaphore.release(); + } + + public TransactionReceipt receipt; + public Semaphore semaphore = new Semaphore(1, true); + }; - public TransactionReceipt receipt; - public Semaphore semaphore = new Semaphore(1, true); - }; + protected TransactionReceipt executeTransaction(Function function) + throws IOException, TransactionException { Callback callback = new Callback(); - asyncExecuteTransaction(function, callback); + asyncExecuteTransaction(FunctionEncoder.encode(function), function.getName(), callback); try { callback.semaphore.acquire(1); } catch (InterruptedException e) { @@ -380,22 +383,38 @@ public void onResponse(TransactionReceipt receipt) { protected TransactionReceipt executeTransaction( String data, BigInteger weiValue, String funcName) throws TransactionException, IOException { - TransactionReceipt receipt = - send( - contractAddress, - data, - weiValue, - gasProvider.getGasPrice(funcName), - gasProvider.getGasLimit(funcName)); + Callback callback = new Callback(); + + asyncExecuteTransaction(data, funcName, callback); + try { + callback.semaphore.acquire(1); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + TransactionReceipt receipt = callback.receipt; if (!receipt.isStatusOK()) { String status = receipt.getStatus(); BigInteger gasUsed = receipt.getGasUsed(); + + /* String message = + String.format( + "Transaction has failed with status: %s. " + + "Gas used: %d. (not-enough gas?)", + status, gasUsed);*/ String message = - String.format( - "Transaction has failed with status: %s. " - + "Gas used: %d. (not-enough gas?)", - status, gasUsed); + StatusCode.getStatusMessage(receipt.getStatus(), receipt.getMessage()) + + " .gas used: " + + gasUsed.toString(); + + logger.trace( + " execute transaction not successfully, hash: {}, status: {}, message: {}, gasUsed: {}", + receipt.getTransactionHash(), + receipt.getStatus(), + receipt.getMessage(), + receipt.getGasUsed()); + throw new TransactionException(message, status, gasUsed, receipt.getTransactionHash()); } @@ -403,21 +422,36 @@ protected TransactionReceipt executeTransaction( } protected void asyncExecuteTransaction(Function function, TransactionSucCallback callback) { + try { - sendOnly( - contractAddress, - FunctionEncoder.encode(function), - BigInteger.ZERO, - gasProvider.getGasPrice(function.getName()), - gasProvider.getGasLimit(function.getName()), - callback); + asyncExecuteTransaction(FunctionEncoder.encode(function), function.getName(), callback); } catch (IOException e) { - // e.print_Stack_Trace(); - logger.error(" IOException, message:{}", e.getMessage()); + logger.error( + " IOException, contractAddress:{}, exception:{} ", + getContractAddress(), + e.getMessage()); } catch (TransactionException e) { - // e.print_Stack_Trace(); - logger.error(" TransactionException, message:{}", e.getMessage()); + logger.error( + " TransactionException, contractAddress:{}, transactionHash: {}, transactionStatus:{}, exception:{} ", + getContractAddress(), + e.getTransactionHash(), + e.getStatus(), + e.getMessage()); } + + // asyncExecuteTransaction(FunctionEncoder.encode(function), function.getName(), callback); + } + + protected void asyncExecuteTransaction( + String data, String funName, TransactionSucCallback callback) + throws IOException, TransactionException { + sendOnly( + contractAddress, + data, + BigInteger.ZERO, + gasProvider.getGasPrice(funName), + gasProvider.getGasLimit(funName), + callback); } protected String createTransactionSeq(Function function) { @@ -457,6 +491,7 @@ protected RemoteCall executeRemoteCallTransaction(Function f private static T create( T contract, String binary, String encodedConstructor, BigInteger value) throws IOException, TransactionException { + TransactionReceipt transactionReceipt = contract.executeTransaction(binary + encodedConstructor, value, FUNC_DEPLOY); @@ -852,32 +887,6 @@ protected List extractEventParametersWithLog(Event event, Li .collect(Collectors.toList()); } - /** - * Subclasses should implement this method to return pre-existing addresses for deployed - * contracts. - * - * @param networkId the network id, for example "1" for the main-net, "3" for ropsten, etc. - * @return the deployed address of the contract, if known, and null otherwise. - */ - protected String getStaticDeployedAddress(String networkId) { - return null; - } - - public final void setDeployedAddress(String networkId, String address) { - if (deployedAddresses == null) { - deployedAddresses = new HashMap<>(); - } - deployedAddresses.put(networkId, address); - } - - public final String getDeployedAddress(String networkId) { - String addr = null; - if (deployedAddresses != null) { - addr = deployedAddresses.get(networkId); - } - return addr == null ? getStaticDeployedAddress(networkId) : addr; - } - /** Adds a log field to {@link EventValues}. */ public static class EventValuesWithLog { private final EventValues eventValues; diff --git a/src/main/java/org/fisco/bcos/web3j/tx/TransactionManager.java b/src/main/java/org/fisco/bcos/web3j/tx/TransactionManager.java index 84183e5ec..9bca19b55 100644 --- a/src/main/java/org/fisco/bcos/web3j/tx/TransactionManager.java +++ b/src/main/java/org/fisco/bcos/web3j/tx/TransactionManager.java @@ -45,6 +45,7 @@ protected TransactionManager( this(new PollingTransactionReceiptProcessor(web3j, sleepDuration, attempts), credentials); } + @Deprecated protected TransactionReceipt executeTransaction( BigInteger gasPrice, BigInteger gasLimit, @@ -110,6 +111,7 @@ public String getFromAddress() { return credentials.getAddress(); } + @Deprecated private TransactionReceipt processResponse(SendTransaction transactionResponse) throws IOException, TransactionException { if (transactionResponse.hasError()) { diff --git a/src/test/java/org/fisco/bcos/channel/test/protocol/EnumNodeVersionTest.java b/src/test/java/org/fisco/bcos/channel/test/protocol/EnumNodeVersionTest.java new file mode 100644 index 000000000..c386a7423 --- /dev/null +++ b/src/test/java/org/fisco/bcos/channel/test/protocol/EnumNodeVersionTest.java @@ -0,0 +1,135 @@ +package org.fisco.bcos.channel.test.protocol; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.fisco.bcos.channel.protocol.ChannelPrococolExceiption; +import org.fisco.bcos.fisco.EnumNodeVersion; +import org.junit.Test; + +public class EnumNodeVersionTest { + + @Test + public void EnumNodeVersionTestFunc0() { + try { + assertThat( + EnumNodeVersion.channelProtocolHandleShakeSupport( + EnumNodeVersion.BCOS_2_0_0_RC1.getVersion()), + is(false)); + assertThat( + EnumNodeVersion.channelProtocolHandleShakeSupport( + EnumNodeVersion.BCOS_2_0_0_RC2.getVersion()), + is(false)); + assertThat( + EnumNodeVersion.channelProtocolHandleShakeSupport( + EnumNodeVersion.BCOS_2_0_0_RC3.getVersion()), + is(false)); + assertThat( + EnumNodeVersion.channelProtocolHandleShakeSupport( + EnumNodeVersion.BCOS_2_0_0.getVersion()), + is(false)); + assertThat( + EnumNodeVersion.channelProtocolHandleShakeSupport( + EnumNodeVersion.BCOS_2_0_1.getVersion()), + is(false)); + assertThat( + EnumNodeVersion.channelProtocolHandleShakeSupport( + EnumNodeVersion.BCOS_2_1_0.getVersion()), + is(true)); + } catch (ChannelPrococolExceiption e) { + assertThat(true, is(false)); + } + } + + @Test + public void EnumNodeVersionTestFunc1() { + try { + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("1.1.0"), is(false)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("1.2.3"), is(false)); + + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.0.0-rc1"), is(false)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.0.0-rc2"), is(false)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.0.0-rc3"), is(false)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.0.0-rc4"), is(false)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.0.0-rc5"), is(false)); + + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.0.0"), is(false)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.0.1"), is(false)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.0.2"), is(false)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.0.3"), is(false)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.0.4"), is(false)); + + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.1.0"), is(true)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.1.1"), is(true)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.1.2"), is(true)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.1.12"), is(true)); + + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.2.0."), is(true)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.2.0."), is(true)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.2.0"), is(true)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.3.1-aaa"), is(true)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.4.2"), is(true)); + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.4.3-rc1"), is(true)); + + } catch (ChannelPrococolExceiption e) { + assertThat(true, is(false)); + } + } + + @Test + public void EnumNodeVersionTestFunc2() { + boolean excp0 = false; + try { + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport(""), is(false)); + } catch (ChannelPrococolExceiption e) { + excp0 = true; + } + + assertThat(excp0, is(true)); + + boolean excp1 = false; + try { + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("aaaa"), is(false)); + } catch (ChannelPrococolExceiption e) { + excp1 = true; + } + + assertThat(excp1, is(true)); + + boolean excp2 = false; + try { + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.4.2.aa"), is(true)); + } catch (ChannelPrococolExceiption e) { + excp2 = true; + } + + assertThat(excp2, is(false)); + + boolean excp3 = false; + try { + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("a.b.c"), is(true)); + } catch (ChannelPrococolExceiption e) { + excp3 = true; + } + + assertThat(excp3, is(true)); + + boolean excp4 = false; + try { + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2"), is(true)); + } catch (ChannelPrococolExceiption e) { + excp4 = true; + } + + assertThat(excp4, is(true)); + + boolean excp5 = false; + try { + assertThat(EnumNodeVersion.channelProtocolHandleShakeSupport("2.1"), is(true)); + } catch (ChannelPrococolExceiption e) { + excp5 = true; + } + + assertThat(excp5, is(true)); + } +} diff --git a/src/test/java/org/fisco/bcos/web3j/tx/txdecode/TransactionDecoderTest.java b/src/test/java/org/fisco/bcos/web3j/tx/txdecode/TransactionDecoderTest.java index faf713d92..a78eaa977 100644 --- a/src/test/java/org/fisco/bcos/web3j/tx/txdecode/TransactionDecoderTest.java +++ b/src/test/java/org/fisco/bcos/web3j/tx/txdecode/TransactionDecoderTest.java @@ -936,7 +936,7 @@ public void testEventDArray() throws BaseException, IOException { assertThat( transEntitytoType0(mapResult1.get(decodeMethodSign(abiDefinition)).get(0)), is(eventDataParams1)); - System.out.println("111 => " + decode.decodeEventReturnJson(logList1)); + // System.out.println("111 => " + decode.decodeEventReturnJson(logList1)); assertThat( decode.decodeEventReturnJson(logList1), is( @@ -953,7 +953,7 @@ public void testEventDArray() throws BaseException, IOException { assertThat( transEntitytoType0(mapResult2.get(decodeMethodSign(abiDefinition)).get(1)), is(eventDataParams2)); - System.out.println("222 => " + decode.decodeEventReturnJson(logList2)); + // System.out.println("222 => " + decode.decodeEventReturnJson(logList2)); assertThat( decode.decodeEventReturnJson(logList2), is( @@ -974,7 +974,7 @@ public void testEventDArray() throws BaseException, IOException { assertThat( transEntitytoType0(mapResult3.get(decodeMethodSign(abiDefinition)).get(2)), is(eventDataParams3)); - System.out.println("333 => " + decode.decodeEventReturnJson(logList3)); + // System.out.println("333 => " + decode.decodeEventReturnJson(logList3)); assertThat( decode.decodeEventReturnJson(logList3), is( @@ -1097,7 +1097,7 @@ public void testEventSArray() throws BaseException, IOException { assertThat( transEntitytoType0(mapResult1.get(decodeMethodSign(abiDefinition)).get(0)), is(eventDataParams1)); - System.out.println("111 => " + decode.decodeEventReturnJson(logList1)); + // System.out.println("111 => " + decode.decodeEventReturnJson(logList1)); assertThat( decode.decodeEventReturnJson(logList1), is( @@ -1114,7 +1114,7 @@ public void testEventSArray() throws BaseException, IOException { assertThat( transEntitytoType0(mapResult2.get(decodeMethodSign(abiDefinition)).get(1)), is(eventDataParams2)); - System.out.println("222 => " + decode.decodeEventReturnJson(logList2)); + // System.out.println("222 => " + decode.decodeEventReturnJson(logList2)); assertThat( decode.decodeEventReturnJson(logList2), is(