Skip to content

Commit

Permalink
Merge pull request #412 from bloxbean/feat/validator_improvements
Browse files Browse the repository at this point in the history
Blueprint Code generation enhancements
  • Loading branch information
satran004 authored Aug 7, 2024
2 parents b3e851c + 0b30927 commit 86e32f5
Show file tree
Hide file tree
Showing 10 changed files with 1,127 additions and 69 deletions.
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

0 comments on commit 86e32f5

Please sign in to comment.