Skip to content

Commit

Permalink
Merge branch 'master' into feat/refactor-value-rename-plus-add-utilit…
Browse files Browse the repository at this point in the history
…y-methods
  • Loading branch information
satran004 committed Nov 7, 2024
2 parents d71ad79 + c3d865f commit adebe93
Show file tree
Hide file tree
Showing 25 changed files with 794 additions and 117 deletions.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@

A client library for Cardano in Java. This library simplifies the interaction with Cardano blockchain from a Java application.

### **Latest Stable Version**: [0.5.1](https://github.com/bloxbean/cardano-client-lib/releases/tag/v0.5.1)

### **Latest Beta**: [0.6.0-beta1](https://github.com/bloxbean/cardano-client-lib/releases/tag/v0.6.0-beta1) (Conway Era Ready)
### **Latest Stable Version**: [0.6.2](https://github.com/bloxbean/cardano-client-lib/releases/tag/v0.6.2)

### More details --> [Documentation](https://cardano-client.dev/)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
package com.bloxbean.cardano.client.spec;

import co.nstant.in.cbor.CborException;
import co.nstant.in.cbor.model.Array;
import co.nstant.in.cbor.model.ByteString;
import co.nstant.in.cbor.model.DataItem;
import co.nstant.in.cbor.model.UnsignedInteger;
import com.bloxbean.cardano.client.common.cbor.CborSerializationUtil;
import com.bloxbean.cardano.client.exception.CborRuntimeException;
import com.bloxbean.cardano.client.exception.CborSerializationException;
import com.bloxbean.cardano.client.util.HexUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
Expand All @@ -33,26 +27,6 @@ default byte[] serialize() throws CborSerializationException {
return finalBytes;
}

/**
* Get serialized bytes for script reference. This is used in TransactionOutput's script_ref
* @return
* @throws CborSerializationException
*/
default byte[] scriptRefBytes() throws CborSerializationException {
int type = getScriptType();
byte[] serializedBytes = serializeScriptBody();

Array array = new Array();
array.add(new UnsignedInteger(type));
array.add(new ByteString(serializedBytes));

try {
return CborSerializationUtil.serialize(array);
} catch (CborException e) {
throw new CborRuntimeException(e);
}
}

@JsonIgnore
default byte[] getScriptHash() throws CborSerializationException {
return blake2bHash224(serialize());
Expand All @@ -63,6 +37,13 @@ default String getPolicyId() throws CborSerializationException {
return HexUtil.encodeHexString(getScriptHash());
}

/**
* Get serialized bytes for script reference. This is used in TransactionOutput's script_ref
* @return byte[]
* @throws CborSerializationException
*/
byte[] scriptRefBytes() throws CborSerializationException;

DataItem serializeAsDataItem() throws CborSerializationException;
byte[] serializeScriptBody() throws CborSerializationException;
@JsonIgnore
Expand Down
33 changes: 33 additions & 0 deletions common/src/main/java/com/bloxbean/cardano/client/util/Try.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.bloxbean.cardano.client.util;

import java.util.function.Supplier;

/**
* A utility class that encapsulates a computation that may either result in a successful value
* or throw an exception. This class provides a way to handle checked exceptions in a functional
Expand Down Expand Up @@ -59,6 +61,37 @@ public T get() {
return result;
}

/**
* Returns the result of the computation if successful, or the specified default value
* if an exception occurred during the computation.
*
* @param defaultValue The value to return if the computation resulted in an exception.
* @return The result of the computation, or the provided default value if an exception occurred.
*/
public T getOrElse(T defaultValue) {
if (exception != null) {
return defaultValue;
}
return result;
}

/**
* Returns the result of the computation if successful, or throws the specified exception
* if an exception was thrown during the computation.
*
* @param exceptionSupplier A Supplier that provides the exception to be thrown if
* the computation resulted in an exception.
* @return The result of the computation.
* @throws RuntimeException The exception provided by the exceptionSupplier if the
* computation resulted in an exception.
*/
public T orElseThrow(Supplier<? extends RuntimeException> exceptionSupplier) {
if (exception != null) {
throw exceptionSupplier.get();
}
return result;
}

/**
* Returns the exception that was thrown during the computation, if any.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
package com.bloxbean.cardano.client.api.util;

import co.nstant.in.cbor.model.Array;
import co.nstant.in.cbor.model.DataItem;
import co.nstant.in.cbor.model.UnsignedInteger;
import com.bloxbean.cardano.client.api.ScriptSupplier;
import com.bloxbean.cardano.client.api.UtxoSupplier;
import com.bloxbean.cardano.client.api.model.Utxo;
import com.bloxbean.cardano.client.common.cbor.CborSerializationUtil;
import com.bloxbean.cardano.client.exception.CborRuntimeException;
import com.bloxbean.cardano.client.plutus.spec.PlutusScript;
import com.bloxbean.cardano.client.spec.Script;
import com.bloxbean.cardano.client.transaction.spec.TransactionInput;
import com.bloxbean.cardano.client.transaction.spec.script.NativeScript;
import com.bloxbean.cardano.client.util.Try;
import com.bloxbean.cardano.client.transaction.spec.Transaction;

import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;

/**
* Utility class for handling reference scripts in transactions.
*/
public class ReferenceScriptUtil {

/**
Expand All @@ -18,19 +29,29 @@ public class ReferenceScriptUtil {
* @param utxoSupplier utxo supplier
* @param scriptSupplier script supplier
* @param transaction transaction
* @return size of the reference scripts
* @return size of the reference scripts in transaction's reference inputs
*/
public static long fetchAndCalculateReferenceScriptsSize(UtxoSupplier utxoSupplier, ScriptSupplier scriptSupplier, Transaction transaction) {
return transaction.getBody().getReferenceInputs()
.stream()
public static long totalRefScriptsSizeInRefInputs(UtxoSupplier utxoSupplier, ScriptSupplier scriptSupplier, Transaction transaction) {
return totalRefScriptsSizeInInputs(utxoSupplier, scriptSupplier, new HashSet<>(transaction.getBody().getReferenceInputs()));
}

/**
* Fetches and calculates the size of reference scripts in given inputs using UTXO supplier and script supplier.
*
* @param utxoSupplier - utxo supplier
* @param scriptSupplier - script supplier
* @param inputs the set of transaction inputs for which to fetch and calculate reference script sizes
* @return the total size of the reference scripts in bytes
*/
public static long totalRefScriptsSizeInInputs(UtxoSupplier utxoSupplier, ScriptSupplier scriptSupplier, Set<TransactionInput> inputs) {
return inputs.stream()
.flatMap(refInput -> utxoSupplier.getTxOutput(refInput.getTransactionId(), refInput.getIndex()).stream()
.map(utxo -> scriptSupplier.getScript(utxo.getReferenceScriptHash()))
.flatMap(Optional::stream))
.map(script -> Try.of(() -> script.scriptRefBytes().length))
.filter(Try::isSuccess)
.mapToLong(Try::get)
.sum();

}

/**
Expand All @@ -40,13 +61,61 @@ public static long fetchAndCalculateReferenceScriptsSize(UtxoSupplier utxoSuppli
* @param transaction transaction
* @return list of {@link PlutusScript}
*/
public static List<PlutusScript> resolveReferenceScripts(UtxoSupplier utxoSupplier, ScriptSupplier scriptSupplier, Transaction transaction) {
return transaction.getBody().getReferenceInputs()
public static List<PlutusScript> resolveReferenceScripts(UtxoSupplier utxoSupplier, ScriptSupplier scriptSupplier, Transaction transaction, Set<Utxo> inputUtxos) {
var refInputPlutusScripts = transaction.getBody().getReferenceInputs()
.stream()
.flatMap(refInput -> utxoSupplier.getTxOutput(refInput.getTransactionId(), refInput.getIndex()).stream()
.map(utxo -> scriptSupplier.getScript(utxo.getReferenceScriptHash()))
.flatMap(Optional::stream))
.collect(Collectors.toList());

var inputPlutusScripts = transaction.getBody().getInputs()
.stream()
.flatMap(input -> inputUtxos.stream()
.filter(utxo -> utxo.getTxHash().equals(input.getTransactionId())
&& utxo.getOutputIndex() == input.getIndex()
&& utxo.getReferenceScriptHash() != null)
.findFirst()
.map(utxo -> scriptSupplier.getScript(utxo.getReferenceScriptHash())).stream()
)
.flatMap(Optional::stream)
.collect(Collectors.toList());

if (refInputPlutusScripts.isEmpty() && inputPlutusScripts.isEmpty())
return Collections.emptyList();
else {
Set<PlutusScript> allPlutusScripts = new HashSet<>();
allPlutusScripts.addAll(refInputPlutusScripts);
allPlutusScripts.addAll(inputPlutusScripts);

return allPlutusScripts.stream().collect(Collectors.toList());
}
}

public static Script deserializeScriptRef(byte[] scriptRefBytes) {
Array scriptArray = (Array) CborSerializationUtil.deserialize(scriptRefBytes);

List<DataItem> dataItemList = scriptArray.getDataItems();
if (dataItemList == null || dataItemList.size() == 0 || dataItemList.size() < 2) {
throw new CborRuntimeException("Reference Script deserialization failed. Invalid no of DataItem : " + dataItemList.size());
}

int type = ((UnsignedInteger) dataItemList.get(0)).getValue().intValue();

try {
switch (type) {
case 0:
return NativeScript.deserializeScriptRef(scriptRefBytes);
case 1:
case 2:
case 3:
return PlutusScript.deserializeScriptRef(scriptRefBytes);
default:
throw new CborRuntimeException("Invalid script type : " + type);
}
} catch (Exception e) {
throw new CborRuntimeException("Reference Script deserialization failed.", e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
import com.bloxbean.cardano.client.api.UtxoSupplier;
import com.bloxbean.cardano.client.api.model.Utxo;
import com.bloxbean.cardano.client.exception.CborSerializationException;
import com.bloxbean.cardano.client.plutus.spec.PlutusV1Script;
import com.bloxbean.cardano.client.plutus.spec.PlutusV2Script;
import com.bloxbean.cardano.client.plutus.spec.PlutusV3Script;
import com.bloxbean.cardano.client.transaction.spec.Transaction;
import com.bloxbean.cardano.client.transaction.spec.TransactionBody;
import com.bloxbean.cardano.client.transaction.spec.TransactionInput;
import com.bloxbean.cardano.client.transaction.spec.script.NativeScript;
import com.bloxbean.cardano.client.transaction.spec.script.ScriptAtLeast;
import com.bloxbean.cardano.client.util.HexUtil;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
Expand All @@ -29,7 +34,7 @@ class ReferenceScriptUtilTest {
private ScriptSupplier scriptSupplier;

@Test
void fetchAndCalculateReferenceScriptsSize() throws CborSerializationException {
void totalRefScriptsSizeInRefInputs() throws CborSerializationException {

var transactionBody = TransactionBody.builder()
.referenceInputs(List.of(
Expand Down Expand Up @@ -77,15 +82,15 @@ void fetchAndCalculateReferenceScriptsSize() throws CborSerializationException {
given(scriptSupplier.getScript("script3")).willReturn(
Optional.of(script3));

var refScriptsSize = ReferenceScriptUtil.fetchAndCalculateReferenceScriptsSize(utxoSupplier, scriptSupplier, transaction);
var refScriptsSize = ReferenceScriptUtil.totalRefScriptsSizeInRefInputs(utxoSupplier, scriptSupplier, transaction);

long expectedSize = script1.scriptRefBytes().length + script2.scriptRefBytes().length + script3.scriptRefBytes().length;

assertThat(refScriptsSize).isEqualTo(expectedSize);
}

@Test
void fetchAndCalculateReferenceScriptsSize_includesNoScriptInTx() throws CborSerializationException {
void totalRefScriptsSizeInRefInputs_includesNoScriptInTx() throws CborSerializationException {

var transactionBody = TransactionBody.builder()
.referenceInputs(List.of(
Expand Down Expand Up @@ -125,15 +130,15 @@ void fetchAndCalculateReferenceScriptsSize_includesNoScriptInTx() throws CborSer
given(scriptSupplier.getScript("script3")).willReturn(
Optional.of(script3));

var refScriptsSize = ReferenceScriptUtil.fetchAndCalculateReferenceScriptsSize(utxoSupplier, scriptSupplier, transaction);
var refScriptsSize = ReferenceScriptUtil.totalRefScriptsSizeInRefInputs(utxoSupplier, scriptSupplier, transaction);

long expectedSize = script1.scriptRefBytes().length + script3.scriptRefBytes().length;

assertThat(refScriptsSize).isEqualTo(expectedSize);
}

@Test
void fetchAndCalculateReferenceScriptsSize_singleScript() throws CborSerializationException {
void totalRefScriptsSizeInRefInputs_singleScript() throws CborSerializationException {

var transactionBody = TransactionBody.builder()
.referenceInputs(List.of(
Expand All @@ -157,15 +162,15 @@ void fetchAndCalculateReferenceScriptsSize_singleScript() throws CborSerializati
given(scriptSupplier.getScript("script1")).willReturn(
Optional.of(script1));

var refScriptsSize = ReferenceScriptUtil.fetchAndCalculateReferenceScriptsSize(utxoSupplier, scriptSupplier, transaction);
var refScriptsSize = ReferenceScriptUtil.totalRefScriptsSizeInRefInputs(utxoSupplier, scriptSupplier, transaction);

long expectedSize = script1.scriptRefBytes().length;

assertThat(refScriptsSize).isEqualTo(expectedSize);
}

@Test
void fetchAndCalculateReferenceScriptsSize_noScript() throws CborSerializationException {
void totalRefScriptsSizeInRefInputs_noScript() throws CborSerializationException {

var transactionBody = TransactionBody.builder()
.referenceInputs(List.of(
Expand All @@ -180,8 +185,75 @@ void fetchAndCalculateReferenceScriptsSize_noScript() throws CborSerializationEx
.willReturn(Optional.of(Utxo.builder()
.build()));

var refScriptsSize = ReferenceScriptUtil.fetchAndCalculateReferenceScriptsSize(utxoSupplier, scriptSupplier, transaction);
var refScriptsSize = ReferenceScriptUtil.totalRefScriptsSizeInRefInputs(utxoSupplier, scriptSupplier, transaction);

assertThat(refScriptsSize).isEqualTo(0);
}

@Test
void deserializeScriptRef_plutusV1() throws CborSerializationException {
PlutusV1Script plutusScript = PlutusV1Script.builder()
.type("PlutusScriptV1")
.cborHex("4e4d01000033222220051200120011")
.build();

var scriptRefBytes = plutusScript.scriptRefBytes();

var script = ReferenceScriptUtil.deserializeScriptRef(scriptRefBytes);

assertThat(script).isInstanceOf(PlutusV1Script.class);
assertThat(script).isEqualTo(plutusScript);
}

@Test
void deserializeScriptRef_plutusV2() throws CborSerializationException {
PlutusV2Script plutusScript = PlutusV2Script.builder()
.type("PlutusScriptV2")
.cborHex("49480100002221200101")
.build();

var scriptRefBytes = plutusScript.scriptRefBytes();

var script = ReferenceScriptUtil.deserializeScriptRef(scriptRefBytes);

assertThat(script).isInstanceOf(PlutusV2Script.class);
assertThat(script).isEqualTo(plutusScript);
}

@Test
void deserializeScriptRef_plutusV3() throws CborSerializationException {
PlutusV3Script plutusScript = PlutusV3Script.builder()
.type("PlutusScriptV3")
.cborHex("46450101002499")
.build();

var scriptRefBytes = plutusScript.scriptRefBytes();

var script = ReferenceScriptUtil.deserializeScriptRef(scriptRefBytes);

assertThat(script).isInstanceOf(PlutusV3Script.class);
assertThat(script).isEqualTo(plutusScript);
}

@Test
void deserializeScriptRef_nativeScript() throws CborSerializationException {
var policy = PolicyUtil.createMultiSigScriptAtLeastPolicy("test", 2, 1);

var scriptRefBytes = policy.getPolicyScript().scriptRefBytes();

var script = ReferenceScriptUtil.deserializeScriptRef(scriptRefBytes);

assertThat(script).isInstanceOf(NativeScript.class);
assertThat(script).isInstanceOf(ScriptAtLeast.class);
assertThat(script).isEqualTo(policy.getPolicyScript());
}

@Test
void deserializeScriptRef_nativeScriptBytes() {
String scriptRefBytesHex = "82008200581ca0dfc5656b946dea62a1fc23ff8881eb6468fdc14c295e2839c3ece3";

var script = ReferenceScriptUtil.deserializeScriptRef(HexUtil.decodeHexString(scriptRefBytesHex));

assertThat(script).isInstanceOf(NativeScript.class);
}
}
Loading

0 comments on commit adebe93

Please sign in to comment.