Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[issue#671] Fix smart contract code generators by generating two more… #672

Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.tx.Contract;
import org.web3j.tx.TransactionManager;
import org.web3j.tx.gas.ContractGasProvider;

import org.web3j.utils.Collection;
import org.web3j.utils.Strings;
import org.web3j.utils.Version;
Expand All @@ -72,6 +74,7 @@ public class SolidityFunctionWrapper extends Generator {
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 GAS_PROVIDER = "gasProvider";
private static final String FILTER = "filter";
private static final String START_BLOCK = "startBlock";
private static final String END_BLOCK = "endBlock";
Expand Down Expand Up @@ -122,9 +125,14 @@ void generateJavaFiles(

TypeSpec.Builder classBuilder = createClassBuilder(className, bin);

classBuilder.addMethod(buildConstructor(Credentials.class, CREDENTIALS));
classBuilder.addMethod(buildConstructor(TransactionManager.class,
TRANSACTION_MANAGER));
classBuilder.addMethod(buildConstructorWithGasPriceAndLimit(
Credentials.class, CREDENTIALS));
classBuilder.addMethod(buildConstructorWithGasPriceAndLimit(
TransactionManager.class, TRANSACTION_MANAGER));
classBuilder.addMethod(buildConstructorWithGasProvider(
Credentials.class, CREDENTIALS));
classBuilder.addMethod(buildConstructorWithGasProvider(
TransactionManager.class, TRANSACTION_MANAGER));
classBuilder.addFields(buildFuncNameConstants(abi));
classBuilder.addMethods(
buildFunctionDefinitions(className, classBuilder, abi));
Expand Down Expand Up @@ -304,17 +312,31 @@ Iterable<FieldSpec> buildFuncNameConstants(List<AbiDefinition> functionDefinitio
return fields;
}

private static MethodSpec buildConstructor(Class authType, String authName) {
private static MethodSpec buildConstructorWithGasPriceAndLimit(
Class authType, String authName) {
return MethodSpec.constructorBuilder()
.addModifiers(Modifier.PROTECTED)
.addParameter(String.class, CONTRACT_ADDRESS)
.addParameter(Web3j.class, WEB3J)
.addParameter(authType, authName)
.addParameter(BigInteger.class, GAS_PRICE)
.addParameter(BigInteger.class, GAS_LIMIT)
.addStatement("super($N, $N, $N, $N, $N, $N)",
BINARY, CONTRACT_ADDRESS, WEB3J, authName, GAS_PRICE, GAS_LIMIT)
.build();
.addModifiers(Modifier.PROTECTED)
.addParameter(String.class, CONTRACT_ADDRESS)
.addParameter(Web3j.class, WEB3J)
.addParameter(authType, authName)
.addParameter(BigInteger.class, GAS_PRICE)
.addParameter(BigInteger.class, GAS_LIMIT)
.addStatement("super($N, $N, $N, $N, $N, $N)",
BINARY, CONTRACT_ADDRESS, WEB3J, authName, GAS_PRICE, GAS_LIMIT)
.build();
}

private static MethodSpec buildConstructorWithGasProvider(
Class authType, String authName) {
return MethodSpec.constructorBuilder()
.addModifiers(Modifier.PROTECTED)
.addParameter(String.class, CONTRACT_ADDRESS)
.addParameter(Web3j.class, WEB3J)
.addParameter(authType, authName)
.addParameter(ContractGasProvider.class, GAS_PROVIDER)
.addStatement("super($N, $N, $N, $N, $N)",
BINARY, CONTRACT_ADDRESS, WEB3J, authName, GAS_PROVIDER)
.build();
}

private MethodSpec buildDeploy(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
package org.web3j.codegen;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.math.BigInteger;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
Expand All @@ -27,12 +44,16 @@
import org.web3j.abi.datatypes.generated.StaticArray3;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.abi.datatypes.generated.Uint64;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.AbiDefinition;
import org.web3j.tx.TransactionManager;
import org.web3j.tx.gas.ContractGasProvider;

import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.web3j.codegen.SolidityFunctionWrapper.buildTypeName;
Expand Down Expand Up @@ -151,7 +172,7 @@ public void testGetEventNativeTypeParameterized() {
public void testBuildFunctionTransaction() throws Exception {
AbiDefinition functionDefinition = new AbiDefinition(
false,
Arrays.asList(
Collections.singletonList(
new AbiDefinition.NamedType("param", "uint8")),
"functionName",
Collections.emptyList(),
Expand All @@ -178,10 +199,10 @@ public void testBuildFunctionTransaction() throws Exception {
public void testBuildingFunctionTransactionThatReturnsValueReportsWarning() throws Exception {
AbiDefinition functionDefinition = new AbiDefinition(
false,
Arrays.asList(
Collections.singletonList(
new AbiDefinition.NamedType("param", "uint8")),
"functionName",
Arrays.asList(
Collections.singletonList(
new AbiDefinition.NamedType("result", "uint8")),
"type",
false);
Expand All @@ -199,7 +220,7 @@ public void testBuildingFunctionTransactionThatReturnsValueReportsWarning() thro
public void testBuildPayableFunctionTransaction() throws Exception {
AbiDefinition functionDefinition = new AbiDefinition(
false,
Arrays.asList(
Collections.singletonList(
new AbiDefinition.NamedType("param", "uint8")),
"functionName",
Collections.emptyList(),
Expand All @@ -226,10 +247,10 @@ public void testBuildPayableFunctionTransaction() throws Exception {
public void testBuildFunctionConstantSingleValueReturn() throws Exception {
AbiDefinition functionDefinition = new AbiDefinition(
true,
Arrays.asList(
Collections.singletonList(
new AbiDefinition.NamedType("param", "uint8")),
"functionName",
Arrays.asList(
Collections.singletonList(
new AbiDefinition.NamedType("result", "int8")),
"type",
false);
Expand All @@ -253,10 +274,10 @@ public void testBuildFunctionConstantSingleValueReturn() throws Exception {
public void testBuildFunctionConstantSingleValueRawListReturn() throws Exception {
AbiDefinition functionDefinition = new AbiDefinition(
true,
Arrays.asList(
Collections.singletonList(
new AbiDefinition.NamedType("param", "uint8")),
"functionName",
Arrays.asList(
Collections.singletonList(
new AbiDefinition.NamedType("result", "address[]")),
"type",
false);
Expand Down Expand Up @@ -288,7 +309,7 @@ public void testBuildFunctionConstantSingleValueRawListReturn() throws Exception
public void testBuildFunctionConstantInvalid() throws Exception {
AbiDefinition functionDefinition = new AbiDefinition(
true,
Arrays.asList(
Collections.singletonList(
new AbiDefinition.NamedType("param", "uint8")),
"functionName",
Collections.emptyList(),
Expand Down Expand Up @@ -433,10 +454,10 @@ public void testBuildEventConstantMultipleValueReturn() throws Exception {
}

@Test
public void testBuildFuncNameConstants() throws Exception {
public void testBuildFuncNameConstants() {
AbiDefinition functionDefinition = new AbiDefinition(
false,
Arrays.asList(
Collections.singletonList(
new AbiDefinition.NamedType("param", "uint8")),
"functionName",
Collections.emptyList(),
Expand All @@ -459,4 +480,137 @@ public void testBuildFuncNameConstants() throws Exception {
assertThat(builder.build().toString(), is(expected));
}

@Test
public void testGenerateJavaFiles() throws IOException, ClassNotFoundException {
String contractName = "HumanStandardToken";
String abiPath = readPathFromClasspath("solidity/contracts/build/HumanStandardToken.abi");
String binPath = readPathFromClasspath("solidity/contracts/build/HumanStandardToken.bin");
String destinationDir = createTempDirectory(
getClass().getSimpleName() + "-testGenerateJavaFiles");
String basePackageName = "test";
solidityFunctionWrapper.generateJavaFiles(
contractName,
binPath,
abiPath,
destinationDir,
basePackageName
);

File generatedJavaFile = new File(
new File(destinationDir, basePackageName),
contractName + ".java");

assertTrue("Should have created a HumanStandardToken.java in "
+ destinationDir + "/" + basePackageName,
generatedJavaFile.exists());

System.out.println("Generated java file is in " + generatedJavaFile);

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, generatedJavaFile.getPath());

URLClassLoader classLoader = URLClassLoader.newInstance(
new URL[] { new File(destinationDir).toURI().toURL() });
Class<?> generatedClass = Class.forName(
basePackageName + "." + contractName, true, classLoader);

assertProtectedDeclaredConstructor(
generatedClass,
new Class[]{
String.class,
Web3j.class,
Credentials.class,
BigInteger.class,
BigInteger.class
});
assertProtectedDeclaredConstructor(
generatedClass,
new Class[]{
String.class,
Web3j.class,
TransactionManager.class,
BigInteger.class,
BigInteger.class
});
assertProtectedDeclaredConstructor(
generatedClass,
new Class[]{
String.class,
Web3j.class,
Credentials.class,
ContractGasProvider.class
});
assertProtectedDeclaredConstructor(
generatedClass,
new Class[]{
String.class,
Web3j.class,
TransactionManager.class,
ContractGasProvider.class
});
}

private String createTempDirectory(String destination) throws IOException {
String testClasspathFile = getTestClasspathFile();
Path destinationPath = Paths.get(testClasspathFile, destination);

if (!Files.exists(destinationPath)) {
Files.createDirectory(destinationPath);
}

Files.walk(destinationPath)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(file -> {
@SuppressWarnings("unused") boolean r = file.delete();
});

return destinationPath.toString();
}

private static void assertProtectedDeclaredConstructor(
Class<?> targetClass, Class[] parameters) {
Constructor<?> constructor = assertDeclaredConstructor(targetClass, parameters);
assertTrue(constructor.getName() + " should have been protected",
Modifier.isProtected(constructor.getModifiers()));
}

private static Constructor<?> assertDeclaredConstructor(
Class<?> targetClass, Class[] parameters) {
try {
return targetClass.getDeclaredConstructor(parameters);
} catch (NoSuchMethodException e) {
String parameterList = Stream.of(parameters)
.map(Class::getSimpleName)
.collect(Collectors.joining(", "));
String constructorName = targetClass.getSimpleName() + "(" + parameterList + ")";
throw new AssertionError(
"Should have generated a constructor `" + constructorName + "`");
}
}

private String getTestClasspathFile() throws IOException {
Enumeration<URL> candidates =
Thread.currentThread().getContextClassLoader().getResources("marker.txt");
while (candidates.hasMoreElements()) {
URL candidate = candidates.nextElement();
if ("file".equalsIgnoreCase(candidate.getProtocol())) {
return new File(candidate.getFile()).getParentFile().getAbsolutePath();
}
}
throw new RuntimeException("Unable to find test classpath. Unable to find marker.txt");
}

private String readPathFromClasspath(String classpath) throws IOException {
try (InputStream fileStream =
Thread.currentThread().getContextClassLoader().getResourceAsStream(classpath)) {
if (fileStream == null) {
throw new FileNotFoundException(
"Unable to find " + classpath + " from any of the classpaths");
}
byte[] allBytes = new byte[fileStream.available()];
@SuppressWarnings("unused") int read = fileStream.read(allBytes);
return new String(allBytes);
}
}
}
1 change: 1 addition & 0 deletions codegen/src/test/resources/marker.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is just a dummy file to help us locate the test classpath
8 changes: 8 additions & 0 deletions core/src/main/java/org/web3j/tx/Contract.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ protected Contract(String contractBinary, String contractAddress,
gasPrice, gasLimit);
}

iikirilov marked this conversation as resolved.
Show resolved Hide resolved
@Deprecated
protected Contract(String contractBinary, String contractAddress,
Web3j web3j, Credentials credentials,
ContractGasProvider gasProvider) {
this(contractBinary, contractAddress, web3j, new RawTransactionManager(web3j, credentials),
gasProvider);
}

@Deprecated
protected Contract(String contractAddress,
Web3j web3j, TransactionManager transactionManager,
Expand Down