StateDatum.java
/*
* UVerify Backend
* Copyright (C) 2025 Fabian Bormann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.uverify.backend.model;
import com.bloxbean.cardano.client.exception.CborDeserializationException;
import com.bloxbean.cardano.client.plutus.spec.*;
import com.bloxbean.cardano.client.util.HexUtil;
import io.uverify.backend.enums.CardanoNetwork;
import io.uverify.backend.util.ValidatorUtils;
import lombok.Getter;
import lombok.Setter;
import java.math.BigInteger;
import java.util.List;
import static io.uverify.backend.util.ValidatorUtils.*;
@Getter
@Setter
public class StateDatum {
private String id;
private String owner;
private Integer fee;
private Integer feeInterval;
private List<byte[]> feeReceivers;
private Long ttl;
private Integer countdown;
private List<UVerifyCertificate> uVerifyCertificates;
private Integer batchSize;
private String bootstrapDatumName;
public static StateDatum fromBootstrapDatum(BootstrapDatum bootstrapDatum, String ownerCredential, List<UVerifyCertificate> uVerifyCertificates) {
StateDatum stateDatum = new StateDatum();
stateDatum.setOwner(ownerCredential);
stateDatum.setFee(bootstrapDatum.getFee());
stateDatum.setFeeInterval(bootstrapDatum.getFeeInterval());
stateDatum.setFeeReceivers(bootstrapDatum.getFeeReceivers());
stateDatum.setTtl(bootstrapDatum.getTtl());
stateDatum.setCountdown(bootstrapDatum.getTransactionLimit());
stateDatum.setUVerifyCertificates(uVerifyCertificates);
stateDatum.setBatchSize(bootstrapDatum.getBatchSize());
stateDatum.setBootstrapDatumName(bootstrapDatum.getTokenName());
return stateDatum;
}
public static StateDatum fromBootstrapDatum(String inlineDatum, byte[] ownerCredential) {
BootstrapDatum bootstrapDatum = BootstrapDatum.fromUtxoDatum(inlineDatum);
return fromBootstrapDatum(bootstrapDatum, HexUtil.encodeHexString(ownerCredential), null);
}
public static StateDatum fromUtxoDatum(String inlineDatum) {
StateDatum stateDatum = new StateDatum();
try {
List<PlutusData> datum = ((ConstrPlutusData) PlutusData.deserialize(HexUtil.decodeHexString(inlineDatum))).getData().getPlutusDataList();
stateDatum.setId(extractByteArrayFromPlutusData(datum.get(0)));
stateDatum.setOwner(extractByteArrayFromPlutusData(datum.get(1)));
stateDatum.setFee(extractIntegerFromPlutusData(datum.get(4)));
stateDatum.setFeeInterval(extractIntegerFromPlutusData(datum.get(5)));
stateDatum.setFeeReceivers(extractListFromPlutusData(datum.get(6)));
stateDatum.setTtl(extractLongFromPlutusData(datum.get(7)));
stateDatum.setCountdown(extractIntegerFromPlutusData(datum.get(8)));
stateDatum.setUVerifyCertificates(UVerifyCertificate.listFromPlutusData(datum.get(9)));
stateDatum.setBatchSize(extractIntegerFromPlutusData(datum.get(10)));
stateDatum.setBootstrapDatumName(extractStringFromPlutusData(datum.get(11)));
} catch (CborDeserializationException e) {
throw new RuntimeException(e);
}
return stateDatum;
}
public static StateDatum fromPreviousStateDatum(StateDatum previousStateDatum) {
StateDatum stateDatum = new StateDatum();
stateDatum.setId(previousStateDatum.getId());
stateDatum.setOwner(previousStateDatum.getOwner());
stateDatum.setFee(previousStateDatum.getFee());
stateDatum.setFeeInterval(previousStateDatum.getFeeInterval());
stateDatum.setFeeReceivers(previousStateDatum.getFeeReceivers());
stateDatum.setTtl(previousStateDatum.getTtl());
stateDatum.setCountdown(previousStateDatum.getCountdown() - 1);
stateDatum.setUVerifyCertificates(previousStateDatum.getUVerifyCertificates());
stateDatum.setBatchSize(previousStateDatum.getBatchSize());
stateDatum.setBootstrapDatumName(previousStateDatum.getBootstrapDatumName());
return stateDatum;
}
public static StateDatum fromPreviousStateDatum(String inlineDatum) {
StateDatum previousStateDatum = fromUtxoDatum(inlineDatum);
return fromPreviousStateDatum(previousStateDatum);
}
public PlutusData toPlutusData(CardanoNetwork network) {
PlutusData uVerifyCertificates = ListPlutusData.of(ConstrPlutusData.builder().alternative(1).data(ListPlutusData.of()).build());
if (this.uVerifyCertificates != null) {
uVerifyCertificates = ListPlutusData.of(
this.uVerifyCertificates.stream()
.map(UVerifyCertificate::toPlutusData)
.toList()
.toArray(new PlutusData[0])
);
}
String stateId = this.id;
if (stateId == null) {
stateId = "00";
}
return ConstrPlutusData.of(0,
BytesPlutusData.of(HexUtil.decodeHexString(stateId)),
BytesPlutusData.of(HexUtil.decodeHexString(this.owner)),
BytesPlutusData.of(HexUtil.decodeHexString(ValidatorUtils.getMintOrBurnAuthTokenHash(network))),
BytesPlutusData.of(HexUtil.decodeHexString(ValidatorUtils.getUpdateStateTokenHash(network))),
BigIntPlutusData.of(BigInteger.valueOf(this.fee)),
BigIntPlutusData.of(BigInteger.valueOf(this.feeInterval)),
ListPlutusData.of(
this.feeReceivers.stream()
.map(BytesPlutusData::of)
.toList()
.toArray(new PlutusData[0])
),
BigIntPlutusData.of(BigInteger.valueOf(this.ttl)),
BigIntPlutusData.of(BigInteger.valueOf(this.countdown)),
uVerifyCertificates,
BigIntPlutusData.of(BigInteger.valueOf(this.batchSize)),
BytesPlutusData.of(this.bootstrapDatumName.getBytes())
);
}
}