Skip to content

Commit

Permalink
Add support for Plutus V3 cost model
Browse files Browse the repository at this point in the history
Refactor UTXO check logic by moving it to BaseTest for reuse. Introduce support for detecting and evaluating Plutus V3 scripts in transactions, ensuring all three versions of Plutus scripts (V1, V2, V3) can be handled.
  • Loading branch information
satran004 committed Sep 20, 2024
1 parent 2c022d3 commit 171d0fc
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.bloxbean.cardano.aiken.tx.evaluator;

import com.bloxbean.cardano.aiken.AikenTransactionEvaluator;
import com.bloxbean.cardano.client.address.AddressProvider;
import com.bloxbean.cardano.client.api.exception.ApiException;
import com.bloxbean.cardano.client.api.model.Amount;
import com.bloxbean.cardano.client.api.model.Result;
import com.bloxbean.cardano.client.api.model.Utxo;
import com.bloxbean.cardano.client.common.model.Networks;
import com.bloxbean.cardano.client.function.helper.ScriptUtxoFinders;
import com.bloxbean.cardano.client.function.helper.SignerProviders;
import com.bloxbean.cardano.client.plutus.spec.BigIntPlutusData;
import com.bloxbean.cardano.client.plutus.spec.PlutusV3Script;
import com.bloxbean.cardano.client.quicktx.QuickTxBuilder;
import com.bloxbean.cardano.client.quicktx.ScriptTx;
import com.bloxbean.cardano.client.quicktx.Tx;
import org.junit.jupiter.api.Test;

import java.math.BigInteger;
import java.util.Optional;
import java.util.Random;

import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Simple script tx example, which
* 1. locks funds to a script address
* 2. unlocks funds from the script address
*/
public class AlwaysTrueScriptV3Test extends BaseTest {

@Test
void alwaysTrueScript() throws ApiException, InterruptedException {
PlutusV3Script plutusScript = PlutusV3Script.builder()
.type("PlutusScriptV3")
.cborHex("46450101002499")
.build();

String scriptAddress = AddressProvider.getEntAddress(plutusScript, Networks.testnet()).toBech32();
BigInteger scriptAmt = new BigInteger("2479280");

Random rand = new Random();
int randInt = rand.nextInt();
BigIntPlutusData plutusData = new BigIntPlutusData(BigInteger.valueOf(randInt)); //any random number

System.out.println("Trying to lock fund --------");
lockFund(scriptAddress, scriptAmt, plutusData);

System.out.println("Trying to unlock fund --------");
//Script tx
Optional<Utxo> optionalUtxo = ScriptUtxoFinders.findFirstByInlineDatum(utxoSupplier, scriptAddress, plutusData);
ScriptTx scriptTx = new ScriptTx()
.collectFrom(optionalUtxo.get(), plutusData)
.payToAddress(senderAddress2, Amount.lovelace(scriptAmt))
.attachSpendingValidator(plutusScript);

QuickTxBuilder quickTxBuilder = new QuickTxBuilder(backendService);
Result<String> result1 = quickTxBuilder.compose(scriptTx)
.feePayer(senderAddress)
.withSigner(SignerProviders.signerFrom(sender))
.withTxEvaluator(new AikenTransactionEvaluator(backendService))
.completeAndWait(System.out::println);

System.out.println(result1.getResponse());
assertTrue(result1.isSuccessful());
}

private void lockFund(String scriptAddress, BigInteger scriptAmt, BigIntPlutusData plutusData) {
Tx tx = new Tx();
tx.payToContract(scriptAddress, Amount.lovelace(scriptAmt), plutusData)
.from(senderAddress2);

QuickTxBuilder quickTxBuilder = new QuickTxBuilder(backendService);
Result<String> result = quickTxBuilder.compose(tx)
.withSigner(SignerProviders.signerFrom(sender2))
.completeAndWait(System.out::println);

System.out.println(result.getResponse());
checkIfUtxoAvailable(result.getValue(), scriptAddress);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
import com.bloxbean.cardano.client.account.Account;
import com.bloxbean.cardano.client.api.ProtocolParamsSupplier;
import com.bloxbean.cardano.client.api.UtxoSupplier;
import com.bloxbean.cardano.client.api.model.Utxo;
import com.bloxbean.cardano.client.backend.api.BackendService;
import com.bloxbean.cardano.client.backend.api.DefaultProtocolParamsSupplier;
import com.bloxbean.cardano.client.backend.api.DefaultUtxoSupplier;
import com.bloxbean.cardano.client.backend.blockfrost.common.Constants;
import com.bloxbean.cardano.client.backend.blockfrost.service.BFBackendService;
import com.bloxbean.cardano.client.backend.koios.KoiosBackendService;
import com.bloxbean.cardano.client.common.model.Networks;

import java.util.List;
import java.util.Optional;

public class BaseTest {
String senderMnemonic = "kit color frog trick speak employ suit sort bomb goddess jewel primary spoil fade person useless measure manage warfare reduce few scrub beyond era";
Account sender = new Account(Networks.testnet(), senderMnemonic);
Expand All @@ -25,4 +28,20 @@ public class BaseTest {
// BackendService backendService = new BFBackendService("http://localhost:8080/api/v1/", "Dummy Key");
UtxoSupplier utxoSupplier = new DefaultUtxoSupplier(backendService.getUtxoService());
ProtocolParamsSupplier protocolParamsSupplier = new DefaultProtocolParamsSupplier(backendService.getEpochService());

protected void checkIfUtxoAvailable(String txHash, String address) {
Optional<Utxo> utxo = Optional.empty();
int count = 0;
while (utxo.isEmpty()) {
if (count++ >= 20)
break;
List<Utxo> utxos = new DefaultUtxoSupplier(backendService.getUtxoService()).getAll(address);
utxo = utxos.stream().filter(u -> u.getTxHash().equals(txHash))
.findFirst();
System.out.println("Try to get new output... txhash: " + txHash);
try {
Thread.sleep(1000);
} catch (Exception e) {}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.bloxbean.cardano.client.api.model.Amount;
import com.bloxbean.cardano.client.api.model.Result;
import com.bloxbean.cardano.client.api.model.Utxo;
import com.bloxbean.cardano.client.backend.api.DefaultUtxoSupplier;
import com.bloxbean.cardano.client.common.CardanoConstants;
import com.bloxbean.cardano.client.common.model.Networks;
import com.bloxbean.cardano.client.function.helper.ScriptUtxoFinders;
Expand All @@ -22,7 +21,6 @@
import org.junit.jupiter.api.Test;

import java.math.BigInteger;
import java.util.List;
import java.util.Optional;

//The caller has to guess the sum of 0..datum_value to claim the locked fund.
Expand Down Expand Up @@ -88,20 +86,4 @@ private String lockFundWithInlineDatum(String scriptAddress, PlutusData datum) {
checkIfUtxoAvailable(result.getValue(), scriptAddress);
return result.getValue();
}

protected void checkIfUtxoAvailable(String txHash, String address) {
Optional<Utxo> utxo = Optional.empty();
int count = 0;
while (utxo.isEmpty()) {
if (count++ >= 20)
break;
List<Utxo> utxos = new DefaultUtxoSupplier(backendService.getUtxoService()).getAll(address);
utxo = utxos.stream().filter(u -> u.getTxHash().equals(txHash))
.findFirst();
System.out.println("Try to get new output... txhash: " + txHash);
try {
Thread.sleep(1000);
} catch (Exception e) {}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
import java.util.*;
import java.util.stream.Collectors;

import static com.bloxbean.cardano.client.plutus.spec.Language.PLUTUS_V1;
import static com.bloxbean.cardano.client.plutus.spec.Language.PLUTUS_V2;
import static com.bloxbean.cardano.client.plutus.spec.Language.*;

/**
* Implements TransactionEvaluator to evaluate a transaction to get script costs using Aiken evaluator.
Expand Down Expand Up @@ -148,18 +147,49 @@ public Result<List<EvaluationResult>> evaluateTx(byte[] cbor, Set<Utxo> inputUtx
if (transaction.getWitnessSet().getPlutusV2Scripts() == null)
transaction.getWitnessSet().setPlutusV2Scripts(new ArrayList<>());

Language language =
(transaction.getWitnessSet() != null && transaction.getWitnessSet().getPlutusV1Scripts() != null
&& transaction.getWitnessSet().getPlutusV1Scripts().size() > 0) ?
PLUTUS_V1 : PLUTUS_V2;
var languages = new HashSet<Language>();
//check plutus v1 present
if (transaction.getWitnessSet() != null
&& transaction.getWitnessSet().getPlutusV1Scripts() != null
&& transaction.getWitnessSet().getPlutusV1Scripts().size() > 0) {
languages.add(PLUTUS_V1);
}

//check plutus v2 present
if (transaction.getWitnessSet() != null
&& transaction.getWitnessSet().getPlutusV2Scripts() != null
&& transaction.getWitnessSet().getPlutusV2Scripts().size() > 0) {
languages.add(PLUTUS_V2);
}

//check if plutus v3 present
if (transaction.getWitnessSet() != null
&& transaction.getWitnessSet().getPlutusV3Scripts() != null
&& transaction.getWitnessSet().getPlutusV3Scripts().size() > 0) {
languages.add(PLUTUS_V3);
}

//Check in reference scripts
for (PlutusScript script : additionalScripts) {
if (script == null)
continue;
if (script.getLanguage() == PLUTUS_V1) {
languages.add(PLUTUS_V1);
} else if (script.getLanguage() == PLUTUS_V2) {
languages.add(PLUTUS_V2);
} else if (script.getLanguage() == PLUTUS_V3) {
languages.add(PLUTUS_V3);
} else {
throw new ApiException("Unsupported language: " + script.getLanguage());
}
}

ProtocolParams protocolParams = protocolParamsSupplier.getProtocolParams();
Optional<CostModel> costModelOptional =
CostModelUtil.getCostModelFromProtocolParams(protocolParams, language);
if (!costModelOptional.isPresent())
throw new ApiException("Cost model not found for language: " + language);

CostMdls costMdls = new CostMdls();
costMdls.add(costModelOptional.get());
languages.stream().map(language -> CostModelUtil.getCostModelFromProtocolParams(protocolParams, language))
.filter(Optional::isPresent)
.forEach(costModelOptional -> costMdls.add(costModelOptional.get()));

TxEvaluator txEvaluator = new TxEvaluator(getSlotConfig());
List<Redeemer> redeemers = txEvaluator.evaluateTx(transaction, utxos, additionalScripts, costMdls);
Expand Down

0 comments on commit 171d0fc

Please sign in to comment.