org.fisco.bcos.web3j.codegen.TruffleJsonFunctionWrapperGenerator Maven / Gradle / Ivy
package org.fisco.bcos.web3j.codegen;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import org.fisco.bcos.web3j.protocol.ObjectMapperFactory;
import org.fisco.bcos.web3j.protocol.core.methods.response.AbiDefinition;
import org.fisco.bcos.web3j.tx.ChainId;
import org.fisco.bcos.web3j.utils.Collection;
import org.fisco.bcos.web3j.utils.Strings;
/**
* Java wrapper source code generator for Truffle JSON format. Truffle embeds the Solidity ABI
* formatted JSON in its own format. That format also gives access to the binary code. It also
* contains information about deployment addresses. This should make integration with Truffle
* easier.
*/
public class TruffleJsonFunctionWrapperGenerator extends FunctionWrapperGenerator {
private static final String USAGE =
"truffle generate "
+ "[--javaTypes|--solidityTypes] "
+ ".json "
+ "-p|--package "
+ "-o|--outputDir ";
private String jsonFileLocation;
private TruffleJsonFunctionWrapperGenerator(
String jsonFileLocation,
String destinationDirLocation,
String basePackageName,
boolean useJavaNativeTypes) {
super(new File(destinationDirLocation), basePackageName, useJavaNativeTypes);
this.jsonFileLocation = jsonFileLocation;
}
public static void run(String[] args) throws Exception {
if (args.length < 1 || !"generate".equals(args[0])) {
Console.exitError(USAGE);
} else {
main(Collection.tail(args));
}
}
public static void main(String[] args) throws Exception {
String[] fullArgs;
if (args.length == 5) {
fullArgs = new String[args.length + 1];
fullArgs[0] = JAVA_TYPES_ARG;
System.arraycopy(args, 0, fullArgs, 1, args.length);
} else {
fullArgs = args;
}
if (fullArgs.length != 6) {
Console.exitError(USAGE);
}
boolean useJavaNativeTypes = useJavaNativeTypes(fullArgs[0], USAGE);
String jsonFileLocation = parsePositionalArg(fullArgs, 1);
String destinationDirLocation = parseParameterArgument(fullArgs, "-o", "--outputDir");
String basePackageName = parseParameterArgument(fullArgs, "-p", "--package");
if (Strings.isEmpty(jsonFileLocation)
|| Strings.isEmpty(destinationDirLocation)
|| Strings.isEmpty(basePackageName)) {
Console.exitError(USAGE);
}
new TruffleJsonFunctionWrapperGenerator(
jsonFileLocation, destinationDirLocation, basePackageName, useJavaNativeTypes)
.generate();
}
static Contract loadContractDefinition(File jsonFile) throws IOException {
ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
return objectMapper.readValue(jsonFile, Contract.class);
}
@SuppressWarnings("unchecked")
private void generate() throws IOException, ClassNotFoundException {
File truffleJsonFile = new File(jsonFileLocation);
if (!truffleJsonFile.exists() || !truffleJsonFile.canRead()) {
Console.exitError("Invalid input json file specified: " + jsonFileLocation);
}
String fileName = truffleJsonFile.getName();
String contractName = getFileNameNoExtension(fileName);
Contract c = loadContractDefinition(truffleJsonFile);
if (c == null) {
Console.exitError("Unable to parse input json file");
} else {
String className = Strings.capitaliseFirstLetter(contractName);
System.out.printf("Generating " + basePackageName + "." + className + " ... ");
Map addresses;
if (c.networks != null && !c.networks.isEmpty()) {
addresses =
c.networks
.entrySet()
.stream()
.filter(e -> (e.getValue() != null && e.getValue().getAddress() != null))
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getAddress()));
} else {
addresses = Collections.EMPTY_MAP;
}
new SolidityFunctionWrapper(useJavaNativeTypes)
.generateJavaFiles(
contractName,
c.getBytecode(),
c.getAbi(),
destinationDirLocation.toString(),
basePackageName,
addresses);
System.out.println("File written to " + destinationDirLocation.toString() + "\n");
}
}
/**
* Truffle Contract
*
* Describes a contract exported by and consumable by Truffle, which may include information
* about deployed instances on networks.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"contractName",
"abi",
"bytecode",
"deployedBytecode",
"sourceMap",
"deployedSourceMap",
"source",
"sourcePath",
"ast",
"compiler",
"networks",
"schemaVersion",
"updatedAt"
})
public static class Contract {
@JsonProperty("contractName")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "^[a-zA-Z_][a-zA-Z0-9_]*$")
public String contractName;
@JsonProperty(value = "abi", required = true)
public List abi;
@JsonProperty("bytecode")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "^0x0$|^0x([a-fA-F0-9]{2}|__.{38})+$")
public String bytecode;
@JsonProperty("deployedBytecode")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "^0x0$|^0x([a-fA-F0-9]{2}|__.{38})+$")
public String deployedBytecode;
@JsonProperty("sourceMap")
public String sourceMap;
@JsonProperty("deployedSourceMap")
public String deployedSourceMap;
@JsonProperty("source")
public String source;
@JsonProperty("sourcePath")
public String sourcePath;
@JsonProperty("ast")
public JsonNode ast;
@JsonProperty("compiler")
public Compiler compiler;
@JsonProperty("networks")
public Map networks;
@JsonProperty("schemaVersion")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "[0-9]+\\.[0-9]+\\.[0-9]+")
public String schemaVersion;
@JsonProperty("updatedAt")
@JsonFormat(
shape = JsonFormat.Shape.STRING,
pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
timezone = "GMT")
public Date updatedAt;
public Contract() {}
public Contract(
String contractName,
List abi,
String bytecode,
String deployedBytecode,
String sourceMap,
String deployedSourceMap,
String source,
String sourcePath,
JsonNode ast,
Compiler compiler,
Map networks,
String schemaVersion,
Date updatedAt) {
super();
this.contractName = contractName;
this.abi = abi;
this.bytecode = bytecode;
this.deployedBytecode = deployedBytecode;
this.sourceMap = sourceMap;
this.deployedSourceMap = deployedSourceMap;
this.source = source;
this.sourcePath = sourcePath;
this.ast = ast;
this.compiler = compiler;
this.networks = networks;
this.schemaVersion = schemaVersion;
this.updatedAt = updatedAt;
}
public String getContractName() {
return contractName;
}
public List getAbi() {
return abi;
}
public String getBytecode() {
return bytecode;
}
public NetworkInfo getNetwork(String networkId) {
return networks == null ? null : networks.get(networkId);
}
public String getAddress(String networkId) {
NetworkInfo network = getNetwork(networkId);
return network == null ? null : network.getAddress();
}
/**
* Convenience method to get the deployed address of the contract.
*
* @param network the contract's address on this Ethereum network
* @return the contract's address or null
if there isn't one known.
*/
public String getAddress(Network network) {
return getAddress(Long.toString(network.id));
}
/*
* c.f., ChainId
*
* This should be updated with https://github.com/web3j/web3j/issues/234
*/
enum Network {
olympic(0),
mainnet(ChainId.MAINNET),
morden(ChainId.EXPANSE_MAINNET),
ropsten(ChainId.ROPSTEN),
rinkeby(ChainId.RINKEBY),
kovan(ChainId.KOVAN);
public final long id;
Network(long id) {
this.id = id;
}
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"name", "version"})
public static class Compiler {
@JsonProperty("name")
public String name;
@JsonProperty("version")
public String version;
@JsonIgnore
private Map additionalProperties = new HashMap();
public Compiler() {}
public Compiler(String name, String version) {
super();
this.name = name;
this.version = version;
}
@JsonAnyGetter
public Map getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, JsonNode value) {
this.additionalProperties.put(name, value);
}
public Compiler withAdditionalProperty(String name, JsonNode value) {
this.additionalProperties.put(name, value);
return this;
}
}
// For now we just ignore "events"
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"events", "links", "address"})
public static class NetworkInfo {
@JsonProperty("events")
public Map events;
@JsonProperty("links")
public Map links;
@JsonProperty("address")
public String address;
public NetworkInfo() {}
public NetworkInfo(Map events, Map links, String address) {
super();
this.events = events;
this.links = links;
this.address = address;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
}