Skip to content

Commit

Permalink
Merge pull request #1988 from tonykwok1992/link-binary
Browse files Browse the repository at this point in the history
Add function to link binary with reference libraries in wrappers contract deployment
  • Loading branch information
gtebrean authored Mar 14, 2024
2 parents a1f47f9 + c495a39 commit 13015e5
Show file tree
Hide file tree
Showing 16 changed files with 427 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
public class SolidityFunctionWrapper extends Generator {

private static final String BINARY = "BINARY";
private static final String LIBRARIES_LINKED_BINARY = "librariesLinkedBinary";
private static final String WEB3J = "web3j";
private static final String CREDENTIALS = "credentials";
private static final String CONTRACT_GAS_PROVIDER = "contractGasProvider";
Expand Down Expand Up @@ -262,6 +263,8 @@ public void generateJavaFiles(
buildLoad(className, TransactionManager.class, TRANSACTION_MANAGER, true));
if (!bin.equals(Contract.BIN_NOT_PROVIDED)) {
classBuilder.addMethods(buildDeployMethods(className, classBuilder, abi));
classBuilder.addMethod(buildLinkLibraryMethod());
classBuilder.addMethod(buildGetDeploymentBinaryMethod());
}

addAddressesSupport(classBuilder, addresses);
Expand Down Expand Up @@ -360,11 +363,17 @@ private TypeSpec.Builder createClassBuilder(

String javadoc = CODEGEN_WARNING + getWeb3jVersion();

return TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC)
.addJavadoc(javadoc)
.superclass(contractClass)
.addField(createBinaryDefinition(binary));
TypeSpec.Builder classBuilder =
TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC)
.addJavadoc(javadoc)
.superclass(contractClass)
.addField(createBinaryDefinition(binary));

if (!binary.equals(Contract.BIN_NOT_PROVIDED)) {
classBuilder.addField(createLibrariesLinkedBinaryField());
}
return classBuilder;
}

private String getWeb3jVersion() {
Expand All @@ -380,6 +389,12 @@ private String getWeb3jVersion() {
return "\n<p>Generated with web3j version " + version + ".\n";
}

private FieldSpec createLibrariesLinkedBinaryField() {
return FieldSpec.builder(String.class, LIBRARIES_LINKED_BINARY)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
.build();
}

private FieldSpec createBinaryDefinition(String binary) {
if (binary.length() < 65534) {
return FieldSpec.builder(String.class, BINARY)
Expand Down Expand Up @@ -653,6 +668,26 @@ private Set<String> getDuplicateFunctionNames(List<AbiDefinition> functionDefini
return duplicateNames;
}

private static MethodSpec buildGetDeploymentBinaryMethod() {
MethodSpec.Builder toReturn =
MethodSpec.methodBuilder("getDeploymentBinary")
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
.returns(ClassName.get(String.class));

CodeBlock codeBlock =
CodeBlock.builder()
.beginControlFlow("if ($L != null)", LIBRARIES_LINKED_BINARY)
.addStatement("return $L", LIBRARIES_LINKED_BINARY)
.nextControlFlow("else")
.addStatement("return $L", BINARY)
.endControlFlow()
.build();

toReturn.addCode(codeBlock);

return toReturn.build();
}

List<MethodSpec> buildDeployMethods(
String className,
TypeSpec.Builder classBuilder,
Expand Down Expand Up @@ -849,43 +884,39 @@ private static MethodSpec buildDeployWithParams(
if (isPayable && !withGasProvider) {
methodBuilder.addStatement(
"return deployRemoteCall("
+ "$L.class, $L, $L, $L, $L, $L, encodedConstructor, $L)",
+ "$L.class, $L, $L, $L, $L, getDeploymentBinary(), encodedConstructor, $L)",
className,
WEB3J,
authName,
GAS_PRICE,
GAS_LIMIT,
BINARY,
INITIAL_VALUE);
methodBuilder.addAnnotation(Deprecated.class);
} else if (isPayable && withGasProvider) {
methodBuilder.addStatement(
"return deployRemoteCall("
+ "$L.class, $L, $L, $L, $L, encodedConstructor, $L)",
+ "$L.class, $L, $L, $L, getDeploymentBinary(), encodedConstructor, $L)",
className,
WEB3J,
authName,
CONTRACT_GAS_PROVIDER,
BINARY,
INITIAL_VALUE);
} else if (!isPayable && !withGasProvider) {
methodBuilder.addStatement(
"return deployRemoteCall($L.class, $L, $L, $L, $L, $L, encodedConstructor)",
"return deployRemoteCall($L.class, $L, $L, $L, $L, getDeploymentBinary(), encodedConstructor)",
className,
WEB3J,
authName,
GAS_PRICE,
GAS_LIMIT,
BINARY);
GAS_LIMIT);
methodBuilder.addAnnotation(Deprecated.class);
} else {
methodBuilder.addStatement(
"return deployRemoteCall($L.class, $L, $L, $L, $L, encodedConstructor)",
"return deployRemoteCall($L.class, $L, $L, $L, getDeploymentBinary(), encodedConstructor)",
className,
WEB3J,
authName,
CONTRACT_GAS_PROVIDER,
BINARY);
CONTRACT_GAS_PROVIDER);
}

return methodBuilder.build();
Expand All @@ -899,42 +930,38 @@ private static MethodSpec buildDeployNoParams(
boolean withGasProvider) {
if (isPayable && !withGasProvider) {
methodBuilder.addStatement(
"return deployRemoteCall($L.class, $L, $L, $L, $L, $L, \"\", $L)",
"return deployRemoteCall($L.class, $L, $L, $L, $L, getDeploymentBinary(), \"\", $L)",
className,
WEB3J,
authName,
GAS_PRICE,
GAS_LIMIT,
BINARY,
INITIAL_VALUE);
methodBuilder.addAnnotation(Deprecated.class);
} else if (isPayable && withGasProvider) {
methodBuilder.addStatement(
"return deployRemoteCall($L.class, $L, $L, $L, $L, \"\", $L)",
"return deployRemoteCall($L.class, $L, $L, $L, getDeploymentBinary(), \"\", $L)",
className,
WEB3J,
authName,
CONTRACT_GAS_PROVIDER,
BINARY,
INITIAL_VALUE);
} else if (!isPayable && !withGasProvider) {
methodBuilder.addStatement(
"return deployRemoteCall($L.class, $L, $L, $L, $L, $L, \"\")",
"return deployRemoteCall($L.class, $L, $L, $L, $L, getDeploymentBinary(), \"\")",
className,
WEB3J,
authName,
GAS_PRICE,
GAS_LIMIT,
BINARY);
GAS_LIMIT);
methodBuilder.addAnnotation(Deprecated.class);
} else {
methodBuilder.addStatement(
"return deployRemoteCall($L.class, $L, $L, $L, $L, \"\")",
"return deployRemoteCall($L.class, $L, $L, $L, getDeploymentBinary(), \"\")",
className,
WEB3J,
authName,
CONTRACT_GAS_PROVIDER,
BINARY);
CONTRACT_GAS_PROVIDER);
}

return methodBuilder.build();
Expand Down Expand Up @@ -1456,6 +1483,25 @@ List<MethodSpec> buildFunctions(
return results;
}

MethodSpec buildLinkLibraryMethod() {
MethodSpec.Builder methodBuilder =
MethodSpec.methodBuilder("linkLibraries")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(
ParameterizedTypeName.get(
ClassName.get(List.class),
ClassName.get(Contract.LinkReference.class)),
"references")
.addStatement(
LIBRARIES_LINKED_BINARY
+ " = "
+ "linkBinaryWithReferences("
+ BINARY
+ ", references)");

return methodBuilder.build();
}

private void buildConstantFunction(
AbiDefinition functionDefinition,
MethodSpec.Builder methodBuilder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public static List<Method> extractValidMethods(Class contract) {
&& parametersAreMatching(m)
&& !m.getName().toLowerCase().contains("event")
&& !m.getName().equals("load")
&& !m.getName().equals("kill"))
&& !m.getName().equals("kill")
&& !m.getName().equals("linkLibraries"))
.collect(Collectors.toList());
}

Expand Down
23 changes: 23 additions & 0 deletions codegen/src/test/java/org/web3j/codegen/ContractJsonParseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@

import java.io.File;
import java.net.URL;
import java.util.Collections;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.web3j.abi.datatypes.Address;
import org.web3j.protocol.core.methods.response.AbiDefinition;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.web3j.codegen.TruffleJsonFunctionWrapperGenerator.Contract;
import static org.web3j.codegen.TruffleJsonFunctionWrapperGenerator.loadContractDefinition;
import static org.web3j.tx.Contract.linkBinaryWithReferences;

/** Test that we can parse Truffle Contract from JSON file. */
public class ContractJsonParseTest {
Expand Down Expand Up @@ -80,4 +83,24 @@ public void testParseConvertLib() throws Exception {
abi.getStateMutability(),
"Expected the 'pure' for the state mutability setting");
}

@Test
public void testLinkBinaryWithReferences() throws Exception {
Contract mc = parseContractJson(contractBaseDir, "MetaCoin", "MetaCoin");
assertTrue(mc.getBytecode().contains("__ConvertLib____________________________"));

String linked =
linkBinaryWithReferences(
mc.getBytecode(),
Collections.singletonList(
new org.web3j.tx.Contract.LinkReference(
"./ConvertLib.sol", "ConvertLib", Address.DEFAULT)));
assertFalse(linked.contains("__ConvertLib____________________________"));
assertEquals(
mc.getBytecode()
.replace(
"__ConvertLib____________________________",
Address.DEFAULT.toString().substring(2)),
linked);
}
}
37 changes: 37 additions & 0 deletions codegen/src/test/java/org/web3j/codegen/GeneraterTestUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2024 Web3 Labs Ltd.
*
* 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.
*/
package org.web3j.codegen;

import java.io.IOException;
import java.util.Collections;
import javax.tools.*;

import static org.junit.jupiter.api.Assertions.assertTrue;

public class GeneraterTestUtils {

public static void verifyGeneratedCode(String sourceFile) throws IOException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();

try (StandardJavaFileManager fileManager =
compiler.getStandardFileManager(diagnostics, null, null)) {
Iterable<? extends JavaFileObject> compilationUnits =
fileManager.getJavaFileObjectsFromStrings(
Collections.singletonList(sourceFile));
JavaCompiler.CompilationTask task =
compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits);
assertTrue(task.call(), "Generated contract contains compile time error");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,12 @@ public void testStructOnlyInArray() throws Exception {

@Test
public void testStructOnlyInArrayCompareJavaFile() throws Exception {
compareJavaFile("OnlyInArrayStruct");
compareJavaFile("OnlyInArrayStruct", false);
}

@Test
public void testArraysInStructCompareJavaFileTest() throws Exception {
compareJavaFile("ArraysInStruct");
compareJavaFile("ArraysInStruct", false);
}

@Test
Expand Down Expand Up @@ -199,14 +199,19 @@ public void testEventParametersNoNamed() throws Exception {

@Test
public void testEventParametersNoNamedCompareJavaFile() throws Exception {
compareJavaFile("EventParameters");
compareJavaFile("EventParameters", false);
}

private void compareJavaFile(String inputFileName) throws Exception {
@Test
public void testDeployMethodGenerated() throws Exception {
compareJavaFile("MetaCoin", true);
}

private void compareJavaFile(String inputFileName, boolean useBin) throws Exception {
String contract = inputFileName.toLowerCase();
String packagePath =
generateCode(
emptyList(), contract, inputFileName, JAVA_TYPES_ARG, false, false, false);
emptyList(), contract, inputFileName, JAVA_TYPES_ARG, useBin, false, false);
File fileActual = new File(tempDirPath, packagePath + "/" + inputFileName + ".java");
File fileExpected =
new File(
Expand All @@ -221,6 +226,8 @@ private void compareJavaFile(String inputFileName) throws Exception {
assertEquals(
new String(Files.readAllBytes(fileExpected.toPath())).replaceAll("(\r\n|\n)", ""),
new String(Files.readAllBytes(fileActual.toPath())).replaceAll("(\r\n|\n)", ""));

verifyGeneratedCode(fileActual.getAbsolutePath());
}

private void testCodeGenerationJvmTypes(String contractName, String inputFileName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -971,4 +971,16 @@ public void testBuildFunctionConstantSingleValueReturnAndTransaction() throws Ex
assertEquals(expectedCall, methodSpecs.get(0).toString());
assertEquals(expectedSend, methodSpecs.get(1).toString());
}

@Test
public void testBuildFunctionLinkBinaryWithReferences() throws Exception {
MethodSpec methodSpec = solidityFunctionWrapper.buildLinkLibraryMethod();

String expected =
"public static void linkLibraries(java.util.List<org.web3j.tx.Contract.LinkReference> references) {\n"
+ " librariesLinkedBinary = linkBinaryWithReferences(BINARY, references);\n"
+ "}\n";

assertEquals(methodSpec.toString(), (expected));
}
}
Loading

0 comments on commit 13015e5

Please sign in to comment.