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

Initial implementation of Plutus Blueprint (CIP 57) #266

Merged
merged 2 commits into from
Jun 20, 2023
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
@@ -0,0 +1,44 @@
package com.bloxbean.cardano.client.plutus.blueprint;

import com.bloxbean.cardano.client.plutus.blueprint.exception.PlutusBlueprintException;
import com.bloxbean.cardano.client.plutus.blueprint.model.PlutusContractBlueprint;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
* Loads Plutus contract blueprint from plutus.json file
*/
public class PlutusBlueprintLoader {

/**
* Loads Plutus contract blueprint from plutus.json file
* @param file plutus.json file
* @return PlutusContractBlueprint
*/
public static PlutusContractBlueprint loadBlueprint(File file) {
try (FileInputStream input = new FileInputStream(file)) {
return loadBlueprint(input);
} catch (IOException ex) {
throw new PlutusBlueprintException(ex);
}
}

/**
* Loads Plutus contract blueprint from plutus.json file
* @param is input stream of plutus.json file
* @return PlutusContractBlueprint
*/
public static PlutusContractBlueprint loadBlueprint(InputStream is) {
try {
ObjectMapper objectMapper = new ObjectMapper();
PlutusContractBlueprint plutusContractBlueprint = objectMapper.readValue(is, PlutusContractBlueprint.class);
return plutusContractBlueprint;
} catch (IOException ex) {
throw new PlutusBlueprintException(ex);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.bloxbean.cardano.client.plutus.blueprint;

import co.nstant.in.cbor.CborException;
import co.nstant.in.cbor.model.ByteString;
import com.bloxbean.cardano.client.common.cbor.CborSerializationUtil;
import com.bloxbean.cardano.client.plutus.blueprint.model.PlutusVersion;
import com.bloxbean.cardano.client.plutus.spec.PlutusScript;
import com.bloxbean.cardano.client.plutus.spec.PlutusV1Script;
import com.bloxbean.cardano.client.plutus.spec.PlutusV2Script;
import com.bloxbean.cardano.client.util.HexUtil;

/**
* Plutus blueprint utility class
*/
public class PlutusBlueprintUtil {

/**
* Convert plutus blueprint's compiled code to PlutusScript
* @param compiledCode - compiled code from plutus blueprint
* @param plutusVersion - Plutus version
* @return PlutusScript
*/
public static PlutusScript getPlutusScriptFromCompiledCode(String compiledCode, PlutusVersion plutusVersion) {
//Do double encoding for aiken compileCode
ByteString bs = new ByteString(HexUtil.decodeHexString(compiledCode));
try {
String cborHex = HexUtil.encodeHexString(CborSerializationUtil.serialize(bs));
if (plutusVersion.equals(PlutusVersion.v1)) {
return PlutusV1Script.builder()
.cborHex(cborHex)
.build();
} else if (plutusVersion.equals(PlutusVersion.v2)) {
return PlutusV2Script.builder()
.cborHex(cborHex)
.build();
} else
throw new RuntimeException("Unsupported Plutus version" + plutusVersion);
} catch (CborException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.bloxbean.cardano.client.plutus.blueprint.exception;

public class PlutusBlueprintException extends RuntimeException {

public PlutusBlueprintException(String msg) {
super(msg);
}

public PlutusBlueprintException(String msg, Throwable t) {
super(msg, t);
}

public PlutusBlueprintException(Throwable t) {
super(t);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.bloxbean.cardano.client.plutus.blueprint.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class BlueprintDatum {
//TODO -- datum fields
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.bloxbean.cardano.client.plutus.blueprint.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
satran004 marked this conversation as resolved.
Show resolved Hide resolved
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class BlueprintRedeemer {
//TODO -- redeemer fields
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.bloxbean.cardano.client.plutus.blueprint.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Parameter {
//TODO -- parameter fields
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.bloxbean.cardano.client.plutus.blueprint.model;

import com.bloxbean.cardano.client.plutus.blueprint.PlutusBlueprintUtil;
import com.bloxbean.cardano.client.plutus.spec.PlutusScript;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
* Initial implementation of Plutus Contract Blueprint (CIP-57)
* Current implementation only returns Preamble and validator's compiled code.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class PlutusContractBlueprint {
private Preamble preamble;
private List<Validator> validators;

@JsonIgnore
public PlutusScript getPlutusScript(String title) {
if (validators == null || validators.isEmpty())
return null;

return validators.stream().filter(v -> v.getTitle().equals(title)).findFirst()
.map(v -> {
if (preamble.getPlutusVersion() == PlutusVersion.v1) {
return PlutusBlueprintUtil.getPlutusScriptFromCompiledCode(v.getCompiledCode(), preamble.getPlutusVersion());
} else {
return PlutusBlueprintUtil.getPlutusScriptFromCompiledCode(v.getCompiledCode(), preamble.getPlutusVersion());
}
}).orElse(null);
}
}




Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.bloxbean.cardano.client.plutus.blueprint.model;

public enum PlutusVersion {
v1, v2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.bloxbean.cardano.client.plutus.blueprint.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Preamble {
String title;
String description;
String version;
PlutusVersion plutusVersion;
String license;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.bloxbean.cardano.client.plutus.blueprint.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Validator {
private String title;
private BlueprintDatum datum;
private BlueprintRedeemer redeemer;
private List<Parameter> parameters;
private String compiledCode;
private String hash;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.bloxbean.cardano.client.plutus.blueprint;

import com.bloxbean.cardano.client.plutus.blueprint.model.PlutusContractBlueprint;
import com.bloxbean.cardano.client.plutus.blueprint.model.PlutusVersion;
import com.bloxbean.cardano.client.plutus.spec.PlutusScript;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.InputStream;

import static org.assertj.core.api.Assertions.assertThat;
class PlutusBlueprintLoaderTest {

@Test
void loadBlueprint_fromInputStream() {
InputStream in = this.getClass().getResourceAsStream("/blueprint/helloworld-plutus.json");
PlutusContractBlueprint blueprint = PlutusBlueprintLoader.loadBlueprint(in);

assertThat(blueprint).isNotNull();
assertThat(blueprint.getPreamble().getTitle()).isEqualTo("aiken-lang/hello_world");
assertThat(blueprint.getPreamble().getDescription()).isEqualTo("Aiken contracts for project 'aiken-lang/hello_world'");
assertThat(blueprint.getPreamble().getVersion()).isEqualTo("1.0.0");
assertThat(blueprint.getPreamble().getPlutusVersion()).isEqualTo(PlutusVersion.v2);
assertThat(blueprint.getPreamble().getLicense()).isEqualTo("Apache 2.0");

assertThat(blueprint.getValidators()).hasSize(2);
assertThat(blueprint.getValidators().get(0).getTitle()).isEqualTo("hello_world");
assertThat(blueprint.getValidators().get(0).getCompiledCode()).isEqualTo("58ad0100003232322225333004323253330063372e646e64004dd7198009801002240009210d48656c6c6f2c20576f726c64210013233300100137586600460066600460060089000240206eb8cc008c00c019200022253335573e004294054ccc024cdc79bae300a00200114a226660060066016004002294088c8ccc0040052000003222333300a3370e008004016466600800866e0000d2002300d001001235573c6ea8004526165734ae855d11");
assertThat(blueprint.getValidators().get(0).getHash()).isEqualTo("5e1e8fa84f2b557ddc362329413caa3fd89a1be26bfd24be05ce0a02");

assertThat(blueprint.getValidators().get(1).getTitle()).isEqualTo("validator-2");
assertThat(blueprint.getValidators().get(1).getCompiledCode()).isEqualTo("581801000032223253330043370e00290020a4c2c6eb40095cd1");
assertThat(blueprint.getValidators().get(1).getHash()).isEqualTo("6e1e8fa84f2b557ddc362329413caa3fd89a1be26bfd24be05ce0a02");
}

@Test
void loadBlueprint_fromFile() {
File file = new File(this.getClass().getResource("/blueprint/helloworld-plutus.json").getFile());
PlutusContractBlueprint blueprint = PlutusBlueprintLoader.loadBlueprint(file);

assertThat(blueprint).isNotNull();
assertThat(blueprint.getPreamble().getTitle()).isEqualTo("aiken-lang/hello_world");
assertThat(blueprint.getPreamble().getDescription()).isEqualTo("Aiken contracts for project 'aiken-lang/hello_world'");
assertThat(blueprint.getPreamble().getVersion()).isEqualTo("1.0.0");
assertThat(blueprint.getPreamble().getPlutusVersion()).isEqualTo(PlutusVersion.v2);
assertThat(blueprint.getPreamble().getLicense()).isEqualTo("Apache 2.0");

assertThat(blueprint.getValidators()).hasSize(2);
assertThat(blueprint.getValidators().get(0).getTitle()).isEqualTo("hello_world");
assertThat(blueprint.getValidators().get(0).getCompiledCode()).isEqualTo("58ad0100003232322225333004323253330063372e646e64004dd7198009801002240009210d48656c6c6f2c20576f726c64210013233300100137586600460066600460060089000240206eb8cc008c00c019200022253335573e004294054ccc024cdc79bae300a00200114a226660060066016004002294088c8ccc0040052000003222333300a3370e008004016466600800866e0000d2002300d001001235573c6ea8004526165734ae855d11");
assertThat(blueprint.getValidators().get(0).getHash()).isEqualTo("5e1e8fa84f2b557ddc362329413caa3fd89a1be26bfd24be05ce0a02");

assertThat(blueprint.getValidators().get(1).getTitle()).isEqualTo("validator-2");
assertThat(blueprint.getValidators().get(1).getCompiledCode()).isEqualTo("581801000032223253330043370e00290020a4c2c6eb40095cd1");
assertThat(blueprint.getValidators().get(1).getHash()).isEqualTo("6e1e8fa84f2b557ddc362329413caa3fd89a1be26bfd24be05ce0a02");
}

@Test
void getPlutusScript() {
InputStream in = this.getClass().getResourceAsStream("/blueprint/helloworld-plutus.json");
PlutusContractBlueprint blueprint = PlutusBlueprintLoader.loadBlueprint(in);

PlutusScript plutusScript1 = blueprint.getPlutusScript("hello_world");
PlutusScript plutusScript2 = blueprint.getPlutusScript("validator-2");

assertThat(plutusScript1).isNotNull();
assertThat(plutusScript2).isNotNull();
assertThat(plutusScript1.getCborHex()).contains("58ad0100003232322225333004323253330063372e646e64004dd7198009801002240009210d48656c6c6f2c20576f726c64210013233300100137586600460066600460060089000240206eb8cc008c00c019200022253335573e004294054ccc024cdc79bae300a00200114a226660060066016004002294088c8ccc0040052000003222333300a3370e008004016466600800866e0000d2002300d001001235573c6ea8004526165734ae855d11");
assertThat(plutusScript2.getCborHex()).contains("581801000032223253330043370e00290020a4c2c6eb40095cd1");
assertThat(plutusScript2.getCborHex().length()).isGreaterThan("581801000032223253330043370e00290020a4c2c6eb40095cd1".length());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.bloxbean.cardano.client.plutus.blueprint;

import com.bloxbean.cardano.client.plutus.blueprint.model.PlutusVersion;
import com.bloxbean.cardano.client.plutus.spec.PlutusScript;
import com.bloxbean.cardano.client.plutus.spec.PlutusV1Script;
import com.bloxbean.cardano.client.plutus.spec.PlutusV2Script;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class PlutusBlueprintUtilTest {

@Test
void getPlutusScriptFromCompiledCodeV2() {

PlutusScript plutusScript = PlutusBlueprintUtil.getPlutusScriptFromCompiledCode("581801000032223253330043370e00290020a4c2c6eb40095cd1", PlutusVersion.v2);

assertThat(plutusScript).isInstanceOf(PlutusV2Script.class);
assertThat(plutusScript.getCborHex()).contains("581801000032223253330043370e00290020a4c2c6eb40095cd1");
assertThat(plutusScript.getCborHex().length()).isGreaterThan("581801000032223253330043370e00290020a4c2c6eb40095cd1".length());
}

@Test
void getPlutusScriptFromCompiledCodeV1() {

PlutusScript plutusScript = PlutusBlueprintUtil.getPlutusScriptFromCompiledCode("581801000032223253330043370e00290020a4c2c6eb40095cd1", PlutusVersion.v1);

assertThat(plutusScript).isInstanceOf(PlutusV1Script.class);
assertThat(plutusScript.getCborHex()).contains("581801000032223253330043370e00290020a4c2c6eb40095cd1");
assertThat(plutusScript.getCborHex().length()).isGreaterThan("581801000032223253330043370e00290020a4c2c6eb40095cd1".length());
}
}
Loading