BootstrapDatum.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.address.Address;
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.dto.BootstrapData;
import io.uverify.backend.entity.BootstrapDatumEntity;
import io.uverify.backend.entity.FeeReceiverEntity;
import io.uverify.backend.entity.UserCredentialEntity;
import io.uverify.backend.enums.CardanoNetwork;
import lombok.*;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import static io.uverify.backend.util.ValidatorUtils.*;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BootstrapDatum {
private List<byte[]> allowedCredentials;
private String tokenName;
private Integer fee;
private Integer feeInterval;
private List<byte[]> feeReceivers;
private Long ttl;
private Integer transactionLimit;
private Integer batchSize;
public static BootstrapDatum fromBootstrapData(BootstrapData bootstrapData) {
return new BootstrapDatum(
bootstrapData.getWhitelistedAddresses().stream().map(BootstrapDatum::extractCredentialFromAddress).toList(),
bootstrapData.getName(),
bootstrapData.getFee(),
bootstrapData.getFeeInterval(),
bootstrapData.getFeeReceiverAddresses().stream().map(BootstrapDatum::extractCredentialFromAddress).toList(),
bootstrapData.getTtl(),
bootstrapData.getTransactionLimit(),
bootstrapData.getBatchSize()
);
}
public static BootstrapDatum generateFrom(List<String> feeReceivers) {
return new BootstrapDatum(
List.of(),
"UVerify_Default_Token",
2000000,
10,
feeReceivers.stream().map(BootstrapDatum::extractCredentialFromAddress).toList(),
System.currentTimeMillis() + 365L * 24 * 60 * 60 * 1000,
1000,
1
);
}
public static byte[] extractCredentialFromAddress(String address) {
Optional<byte[]> maybeServiceAccountPaymentCredential = new Address(address).getPaymentCredentialHash();
if (maybeServiceAccountPaymentCredential.isEmpty()) {
throw new IllegalArgumentException("Invalid payment address");
}
return maybeServiceAccountPaymentCredential.get();
}
public static BootstrapDatum fromBootstapDatumEntity(BootstrapDatumEntity bootstrapDatumEntity) {
BootstrapDatum bootstrapDatum = new BootstrapDatum();
bootstrapDatum.setAllowedCredentials(bootstrapDatumEntity.getAllowedCredentials().stream()
.map(UserCredentialEntity::getCredential)
.map(String::getBytes).toList());
bootstrapDatum.setTokenName(bootstrapDatumEntity.getTokenName());
bootstrapDatum.setFee(bootstrapDatumEntity.getFee());
bootstrapDatum.setFeeInterval(bootstrapDatumEntity.getFeeInterval());
bootstrapDatum.setFeeReceivers(bootstrapDatumEntity.getFeeReceivers().stream()
.map(FeeReceiverEntity::getCredential)
.map(String::getBytes).toList());
bootstrapDatum.setTtl(bootstrapDatumEntity.getTtl());
bootstrapDatum.setTransactionLimit(bootstrapDatumEntity.getTransactionLimit());
bootstrapDatum.setBatchSize(bootstrapDatumEntity.getBatchSize());
return bootstrapDatum;
}
public static BootstrapDatum fromUtxoDatum(String inlineDatum) {
BootstrapDatum bootstrapDatum = new BootstrapDatum();
try {
List<PlutusData> datum = ((ConstrPlutusData) PlutusData.deserialize(HexUtil.decodeHexString(inlineDatum))).getData().getPlutusDataList();
bootstrapDatum.setAllowedCredentials(extractListFromPlutusData(datum.get(0)));
bootstrapDatum.setTokenName(extractStringFromPlutusData(datum.get(2)));
bootstrapDatum.setFee(extractIntegerFromPlutusData(datum.get(4)));
bootstrapDatum.setFeeInterval(extractIntegerFromPlutusData(datum.get(5)));
bootstrapDatum.setFeeReceivers(extractListFromPlutusData(datum.get(6)));
bootstrapDatum.setTtl(extractLongFromPlutusData(datum.get(7)));
bootstrapDatum.setTransactionLimit(extractIntegerFromPlutusData(datum.get(8)));
bootstrapDatum.setBatchSize(extractIntegerFromPlutusData(datum.get(9)));
} catch (CborDeserializationException e) {
throw new RuntimeException(e);
}
return bootstrapDatum;
}
public PlutusData toPlutusData(CardanoNetwork network) {
return ConstrPlutusData.of(0,
ListPlutusData.of(
this.allowedCredentials.stream()
.map(BytesPlutusData::of)
.toList()
.toArray(new PlutusData[0])
),
BytesPlutusData.of(HexUtil.decodeHexString(getMintOrBurnAuthTokenHash(network))),
BytesPlutusData.of(this.tokenName),
BytesPlutusData.of(HexUtil.decodeHexString(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.transactionLimit)),
BigIntPlutusData.of(BigInteger.valueOf(this.batchSize))
);
}
}