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

Blueprint Code generation enhancements #412

Merged
merged 4 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ private TypeSpec generateDataImpl(TypeSpec.Builder classBuilder,
ClassName dataClass) {
FieldSpec converterField = FieldSpec.builder(converterClass, "converter")
.addModifiers(Modifier.PRIVATE)
.addModifiers(Modifier.STATIC)
.initializer("new $T()", converterClass)
.build();

MethodSpec constructor = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addStatement("this.converter = new $T()", converterClass)
.build();

return classBuilder
Expand All @@ -64,6 +65,8 @@ private TypeSpec generateDataImpl(TypeSpec.Builder classBuilder,
.addMethod(constructor)
.addMethod(getToPlutusDataMethodSpec(dataClass))
.addMethod(getFromPlutusDataMethodSpec(dataClass))
.addMethod(getDeserializeMethodByString(dataClass))
.addMethod(getDeserializeMethodByBytes(dataClass))
.build();
}

Expand All @@ -80,16 +83,34 @@ private MethodSpec getToPlutusDataMethodSpec(ClassName dataClass) {

private MethodSpec getFromPlutusDataMethodSpec(ClassName dataClass) {
MethodSpec fromPlutusDataMethod = MethodSpec.methodBuilder("fromPlutusData")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(dataClass)
.addParameter(ClassName.get(ConstrPlutusData.class), "data")
.addStatement("return converter.fromPlutusData(data)")
.build();

return fromPlutusDataMethod;
}

private MethodSpec getDeserializeMethodByString(ClassName dataClass) {
MethodSpec deserialize = MethodSpec.methodBuilder("deserialize")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(dataClass)
.addParameter(String.class, "cborHex")
.addStatement("return converter.deserialize(cborHex)")
.build();
return deserialize;
}

private MethodSpec getDeserializeMethodByBytes(ClassName dataClass) {
MethodSpec deserialize = MethodSpec.methodBuilder("deserialize")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(dataClass)
.addParameter(byte[].class, "cborBytes")
.addStatement("return converter.deserialize(cborBytes)")
.build();
return deserialize;
}

private TypeSpec generateEnumDataImpl(TypeSpec.Builder classBuilder,
ClassName converterClass,
ClassName dataClass,
Expand All @@ -101,14 +122,14 @@ private TypeSpec generateEnumDataImpl(TypeSpec.Builder classBuilder,
.build();

FieldSpec converterField = FieldSpec.builder(converterClass, "converter")
.addModifiers(Modifier.PRIVATE)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
.initializer("new $T()", converterClass)
.build();

MethodSpec constructor = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PRIVATE)
.addParameter(dataClass, dataClassFieldName)
.addStatement("this.$N = $N", dataClassFieldName, dataClassFieldName)
.addStatement("this.converter = new $T()", converterClass)
.build();

//--of method
Expand All @@ -127,23 +148,16 @@ private TypeSpec generateEnumDataImpl(TypeSpec.Builder classBuilder,
.addStatement("return converter.toPlutusData($N)", dataClassFieldName)
.build();

//--fromPlutusData
MethodSpec fromPlutusDataMethod = MethodSpec.methodBuilder("fromPlutusData")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.returns(dataClass)
.addParameter(ClassName.get(ConstrPlutusData.class), "data")
.addStatement("return converter.fromPlutusData(data)")
.build();

return classBuilder
.addJavadoc(GENERATED_CODE)
.addField(dataClassFieldSpec)
.addField(converterField)
.addMethod(constructor)
.addMethod(ofMethod)
.addMethod(toPlutusDataMethod)
.addMethod(fromPlutusDataMethod)
.addMethod(getFromPlutusDataMethodSpec(dataClass))
.addMethod(getDeserializeMethodByString(dataClass))
.addMethod(getDeserializeMethodByBytes(dataClass))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.bloxbean.cardano.client.address.AddressProvider;
import com.bloxbean.cardano.client.common.model.Network;
import com.bloxbean.cardano.client.exception.CborRuntimeException;
import com.bloxbean.cardano.client.exception.CborSerializationException;
import com.bloxbean.cardano.client.plutus.annotation.Blueprint;
import com.bloxbean.cardano.client.plutus.annotation.ExtendWith;
import com.bloxbean.cardano.client.plutus.annotation.processor.util.JavaFileUtil;
Expand All @@ -11,6 +13,7 @@
import com.bloxbean.cardano.client.plutus.blueprint.model.Validator;
import com.bloxbean.cardano.client.plutus.spec.PlutusScript;
import com.bloxbean.cardano.client.quicktx.blueprint.extender.AbstractValidatorExtender;
import com.bloxbean.cardano.client.util.HexUtil;
import com.squareup.javapoet.*;

import javax.annotation.processing.ProcessingEnvironment;
Expand Down Expand Up @@ -69,10 +72,38 @@ public void processValidator(Validator validator, PlutusVersion plutusVersion) {
title = JavaFileUtil.toCamelCase(title);

List<FieldSpec> metaFields = ValidatorProcessor.getFieldSpecsForValidator(validator);

FieldSpec networkField = FieldSpec.builder(Network.class, "network")
.addModifiers(Modifier.PRIVATE)
.build();

FieldSpec scriptAddrField = FieldSpec.builder(String.class, "scriptAddress")
.addModifiers(Modifier.PRIVATE)
.build();

FieldSpec plutusScriptField = FieldSpec.builder(PlutusScript.class, "plutusScript")
.addModifiers(Modifier.PRIVATE)
.build();

boolean isParameterizedValidator = isParameterizedValidator(validator);

FieldSpec applyParamCompiledCodeField = null;
FieldSpec applyParamHashField = null;
MethodSpec applyParamCompiledCodeGetter = null;
MethodSpec applyParamHashGetter = null;
if(isParameterizedValidator) {
applyParamCompiledCodeField = FieldSpec.builder(String.class, "applyParamCompiledCode")
.addModifiers(Modifier.PRIVATE)
.build();
applyParamHashField = FieldSpec.builder(String.class, "applyParamHash")
.addModifiers(Modifier.PRIVATE)
.build();

applyParamCompiledCodeGetter = getApplyParamCompiledCodeGetterMethodSpec();
applyParamHashGetter = getApplyParamHashGetterMethodSpec();

}

List<FieldSpec> fields = new ArrayList<>();

//TODO -- Handle parameterized validators
Expand Down Expand Up @@ -107,21 +138,34 @@ public void processValidator(Validator validator, PlutusVersion plutusVersion) {
}

List<MethodSpec> methods = new ArrayList<>();
methods.addAll(createMethodSpecsForGetterSetters(metaFields, true));
methods.addAll(createMethodSpecsForGetterSetters(fields, false));
methods.addAll(createMethodSpecsForGetterSetters(List.of(networkField), false));
methods.add(getScriptAddressMethodSpec(plutusVersion));
methods.add(getPlutusScriptMethodSpec(plutusVersion));
methods.add(getPlutusScriptMethodSpec(plutusVersion, isParameterizedValidator));

if (isParameterizedValidator) {
methods.add(applyParamCompiledCodeGetter);
methods.add(applyParamHashGetter);
}

String validatorClassName = title + VALIDATOR_CLASS_SUFFIX;
// building and saving of class
var builder = TypeSpec.classBuilder(validatorClassName)
.addModifiers(Modifier.PUBLIC)
.addJavadoc(GENERATED_CODE)
.addMethod(getConstructorMethodSpec())
.addMethod(getConstructorMethodSpec(isParameterizedValidator))
.addFields(metaFields)
.addField(networkField)
.addField(scriptAddrField)
.addFields(fields)
.addMethods(methods);
.addField(plutusScriptField);

if(isParameterizedValidator) {
builder.addField(applyParamCompiledCodeField);
builder.addField(applyParamHashField);
}

builder.addMethods(methods);

if (extendWith != null) {
var extendWithTypeMirros = getExtendWithValues(extendWith);
Expand Down Expand Up @@ -150,60 +194,109 @@ public void processValidator(Validator validator, PlutusVersion plutusVersion) {
}

//Create constructor with Network parameter
private MethodSpec getConstructorMethodSpec() {
MethodSpec constructor = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(Network.class, "network")
.addStatement("this.network = network")
.build();
return constructor;
private MethodSpec getConstructorMethodSpec(boolean isParameterizedValidator) {
if (isParameterizedValidator) {
MethodSpec constructor = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(Network.class, "network")
.addParameter(String.class, "applyParamCompiledCode")
.addStatement("this.network = network")
.addStatement("this.applyParamCompiledCode = applyParamCompiledCode")
.build();
return constructor;
} else {
MethodSpec constructor = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(Network.class, "network")
.addStatement("this.network = network")
.build();
return constructor;
}
}

private MethodSpec getScriptAddressMethodSpec(PlutusVersion plutusVersion) {
MethodSpec getScriptAddress = MethodSpec.methodBuilder("getScriptAddress")
.addModifiers(Modifier.PUBLIC)
.returns(String.class)
.addJavadoc("Returns the address of the validator script")
.addStatement("var script = $T.getPlutusScriptFromCompiledCode(this.compiledCode, $T.$L)", PlutusBlueprintUtil.class, plutusVersion.getClass(), plutusVersion)
.addStatement("if(scriptAddress == null) scriptAddress = $T.getEntAddress(script, network).toBech32()", AddressProvider.class)
.beginControlFlow("if(scriptAddress == null)")
.addStatement("var script = getPlutusScript()")
.addStatement("scriptAddress = $T.getEntAddress(script, network).toBech32()", AddressProvider.class)
.endControlFlow()
.addStatement("return scriptAddress")
.build();
return getScriptAddress;
}

private MethodSpec getPlutusScriptMethodSpec(PlutusVersion plutusVersion) {
MethodSpec getPlutusScript = MethodSpec.methodBuilder("getPlutusScript")
private MethodSpec getPlutusScriptMethodSpec(PlutusVersion plutusVersion, boolean isParameterizedValidator) {
var builder = MethodSpec.methodBuilder("getPlutusScript")
.addModifiers(Modifier.PUBLIC)
.returns(PlutusScript.class);

if (isParameterizedValidator) {
//Use beginControl flow to check if plutusScript is null
builder.beginControlFlow("if (plutusScript == null)");
builder.addStatement("plutusScript = $T.getPlutusScriptFromCompiledCode(this.applyParamCompiledCode, $T.$L)", PlutusBlueprintUtil.class, plutusVersion.getClass(), plutusVersion);
builder.endControlFlow();

builder.addStatement("return plutusScript");
} else {
builder.beginControlFlow("if (plutusScript == null)");
builder.addStatement("plutusScript = $T.getPlutusScriptFromCompiledCode(COMPILED_CODE, $T.$L)", PlutusBlueprintUtil.class, plutusVersion.getClass(), plutusVersion);
builder.endControlFlow();
builder.addStatement("return plutusScript");
}

return builder.build();
}

private MethodSpec getApplyParamCompiledCodeGetterMethodSpec() {
MethodSpec getApplyParamCompiledCode = MethodSpec.methodBuilder("getApplyParamCompiledCode")
.addModifiers(Modifier.PUBLIC)
.returns(PlutusScript.class)
.addStatement("return $T.getPlutusScriptFromCompiledCode(this.compiledCode, $T.$L)", PlutusBlueprintUtil.class, plutusVersion.getClass(), plutusVersion)
.returns(String.class)
.addStatement("return applyParamCompiledCode")
.build();
return getPlutusScript;
return getApplyParamCompiledCode;
}

private MethodSpec getApplyParamHashGetterMethodSpec() {
MethodSpec getApplyParamHash = MethodSpec.methodBuilder("getApplyParamHash")
.addModifiers(Modifier.PUBLIC)
.addJavadoc("Returns the hash of the script after applying the parameters\n")
.addJavadoc("@throws CborRuntimeException if there is an error in getting the hash")
.returns(String.class)
.beginControlFlow("if (applyParamHash == null)")

.beginControlFlow("try")
.addStatement("applyParamHash = $T.encodeHexString(getPlutusScript().getScriptHash())", HexUtil.class)
.nextControlFlow("catch ($T e)", CborSerializationException.class)
.addStatement("throw new $T(\"Error getting hash from compiled code\", e)", CborRuntimeException.class)
.endControlFlow()
.endControlFlow()
.addStatement("return applyParamHash")
.build();
return getApplyParamHash;
}

public static List<FieldSpec> getFieldSpecsForValidator(Validator validator) {
List<FieldSpec> fields = new ArrayList<>();
fields.add(FieldSpec.builder(String.class, "title")
.addModifiers(Modifier.PRIVATE) // need to fix AnnotationProcessor for final variables
fields.add(FieldSpec.builder(String.class, "TITLE")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) // need to fix AnnotationProcessor for final variables
.initializer("$S", validator.getTitle())
.build());
fields.add(FieldSpec.builder(String.class, "description")
.addModifiers(Modifier.PRIVATE)
fields.add(FieldSpec.builder(String.class, "DESCRIPTION")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
.initializer("$S", validator.getDescription())
.build());
fields.add(FieldSpec.builder(String.class, "compiledCode")
.addModifiers(Modifier.PRIVATE)
fields.add(FieldSpec.builder(String.class, "COMPILED_CODE")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
.initializer("$S", validator.getCompiledCode())
.build());
fields.add(FieldSpec.builder(String.class, "hash")
.addModifiers(Modifier.PRIVATE)
fields.add(FieldSpec.builder(String.class, "HASH")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
.initializer("$S", validator.getHash())
.build());

fields.add(FieldSpec.builder(Network.class, "network")
.addModifiers(Modifier.PRIVATE)
.build());

return fields;
}

Expand All @@ -226,6 +319,10 @@ private TypeMirror getExtendWithValue(ExtendWith extendWith) {
return null;
}

private static boolean isParameterizedValidator(Validator validator) {
return validator.getParameters() != null && validator.getParameters().size() > 0;
}

private TypeMirror getTypeMirror(String type) {
try {
// This will throw MirroredTypeException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
import com.bloxbean.cardano.client.plutus.spec.ConstrPlutusData;

/**
* Implement this interface to convert an object to PlutusData and vice versa.
* Implement this interface to convert an object to PlutusData.
*
* @param <T>
*/
public interface Data<T> {

ConstrPlutusData toPlutusData();

T fromPlutusData(ConstrPlutusData data);
}
Loading
Loading