ConnectedGoodUtils.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.extension.utils;

import com.bloxbean.cardano.client.exception.CborSerializationException;
import com.bloxbean.cardano.client.plutus.blueprint.PlutusBlueprintUtil;
import com.bloxbean.cardano.client.plutus.blueprint.model.PlutusVersion;
import com.bloxbean.cardano.client.plutus.spec.PlutusScript;
import com.bloxbean.cardano.yaci.store.common.domain.AddressUtxo;
import com.bloxbean.cardano.yaci.store.common.domain.Amt;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

public class ConnectedGoodUtils {
    private static final String CONNECTED_GOODS_CODE = "590949010100323232323232323225333002323232323232323232323232323232323253232333015300800e1323232323232323232533301e3011301f375400226464a666040602660426ea80044c8c8c94ccc098c0a40084c8c94ccc094cdc3a4008604c6ea80044c8cc06000454ccc098c064c09cdd51980a007919b8f372666e28dd7180b98149baa3017302937540026e60c8c8c8c8c8dcc999800800981018171baa301c302e375400c9110022253332333303600313232323232323300c002001337149110128000025333033337100069007099b80483c80400c54ccc0cccdc4001a410004266e00cdc0241002800690068b299981a800899b8a4881035b5d2900005133714911035b5f2000333300800133714911025d290000522333009009002300600122333009009002001375860660046eb4c0c4004c8cdd81ba83031001374e60640026ea800c4c94ccc0cc0044cdc52441027b7d00003133714911037b5f200032323300100100322533303600110031533303630390011323330090093035001337149101023a2000333009009303600100430380011323330090093035001337149101023a20003330090093036001300633003003303a00230380013371491102207d000033756006264a666066002266e29221025b5d00003133714911035b5f2000333300600133714911015d000032233300700700230040012233300700700200137580066e292201022c2000133005375a0040022646466e2922010268270000132333001001337006e3400920013371491101270000322253330323371000490000800899191919980300319b8000548004cdc599b80002533303533710004900a0a40c02903719b8b33700002a66606a66e2000520141481805206e0043370c004901019b8300148080cdc70020011bae0022222323300100100522533303300110051533303330360011330033035001005133004303500133002002303600122323300100100322533302d30200011337149110130000031533302d337100029000099b8a489012d003300200233702900000089980299b8400148050cdc599b803370a002900a240c00066002002444a66605466e2400920001001133300300333708004900a19b8b3370066e14009201448180004dd7180b98149baa002153330263013003153330263301b301c3756603460506ea801804454ccc098c04ccc8c004004894ccc0ac004520001337009001198010011817000805099b8f0084891c3f9ff01fd67fcf42cb64f004f13306bd4bfd651154aedcb0dd68dd870014a029405280b181518139baa001163015302637540086eb4c09400458c09c004c94ccc088c03cc08cdd50008a5eb7bdb1804dd5981398121baa001323300100100722533302600114c103d87a80001323232325333027337220240042a66604e66e3c0480084c058cc0acdd3000a5eb80530103d87a8000133006006003375660500066eb8c098008c0a8008c0a0004c094c088dd50008b19806803119baf301030223754602060446ea8004c030cc090dd4805a5eb80dd7181198101baa00116533302000114c103d87a80001300c3302130220014bd701bac3021302230223022302200237566040002604060400046eb0c078004c078c078008dd6180e000980c1baa013375c6034602e6ea803c54ccc054c0080384c8c8c94ccc060c02cc064dd5009899191919299980f98110010a99980e1807980e9baa00513232323232533302130143022375400226464a666046602060486ea80044c8c8c8c8c94ccc0a0c06cc0a4dd5000899192999815180e98159baa001132323232533302e3370e900218179baa00113233021001132325333031301e300100c15333031301e300100b15333031323300100130283756604c60686ea8028894ccc0d8004528899299981a19299981a99b8f00101214a22a66606a66e3c0052201086c6f76656c6163650014a2266e3c00522100375c60720042660060060022940c0e400454ccc0c4cc098c09cdd5981298199baa00900e153330313302630273756604a60666ea8c094c0ccdd50080070a99981899b8f375c604a60666ea801cdc99bae3025303337540582a66606266ebcdd399991800800911299981b8008801099980180199804801181c800981d0009bac30253033375400a6eb0c094c0ccdd5001a6101800013370e60026eb0c094c0ccdd500298009bac30253033375400629405280a5014a029405280a5030010012253330340011480004cdc02400466004004606e002606660606ea800458c078c0bcdd5002980d998182610544613130320033030374e660026eb0c080c0b8dd500800125eb8088c8cc00400400c894ccc0c800452f5c026464a66606266ebc00801440044cc0d4008cc010010004c0d8008c0d0004c0bcc0b0dd50008b1980b9bac301d302b375401a466e3cdd7180d18161baa001375c603460586ea8094c0b4c0a8dd50008b29998150008a6103d87a8000130163302b302c0014bd7019801003919baf301730293754602e60526ea8004c04ccc0acdd480225eb80cc00401c8cdd7980b18141baa301630283754603460506ea8004c048cc0a8dd4801a5eb8088c8cc00400400c894ccc0ac00452f5c026464a666054600a00426605c00466008008002266008008002605e004605a0026eb8c0a0c094dd50008b180918121baa301230243754602c60486ea8004c098c08cdd50008b19807001119baf3011302337540020166eb0c090c094c094008dd61811800980f9baa01a3021301e375400a2c2c6eb8c080004c080008dd7180f000980d1baa01316301c301d002301b0013017375401e2c6e952002370e900111191980080080191299980c8008a6103d87a80001323253330183005002130073301c0024bd70099802002000980e801180d8009ba5480008c0580048c054c058c058004894ccc040c00cc044dd5001099191919299980b980d001099198008008011119299980d0010a804099299980c1805980c9baa001132323232533301f302200213006302200716375c604000260400046eb8c078004c068dd50008b180e00118010010b1bac30180013018002375c602c00260246ea800858dc3a400046024602600244646600200200644a66602400229404c94ccc040cdc79bae301500200414a2266006006002602a00246004002600200244a66601a002297ae013300e300b300f001330020023010001300837540026016601800460140026014004601000260086ea8004526136565734aae7555cf2ab9f5740ae855d12ba401";
    private static final String SOCIAL_HUB_CODE = "590a740101003232323232323225333002323232323232323232323232323232323232323232323232323232323253232333021301001a132323232323232323232533302b301a302c37540022646464a66605c603a605e6ea80044c8c94ccc0c0c044c0c4dd50008992999818981018191baa0011323232325333038303b002132330010010022232533303b00213253330393028303a3754002264a666074605260766ea80044c8c8c8c94ccc104c1100084c8c94ccc10cc1180084c94ccc104cdc79bae3042002375c604860866ea80204c94ccc108c08cc10cdd5000899198158008a99982199b8f375c6050608a6ea8004dd7181318229baa01315333043302d302e01715333043302d0031533304330300011533304333036303737566050608a6ea806008854ccc10cc0b4c0e806c4cc0d806cdd7181318229baa00114a029405280a5014a02940c11cc110dd50008b181918219baa01616375a60840022c608800264a66607e605260806ea800452f5bded8c026eacc110c104dd5000991980080080c9129998218008a6103d87a80001323232325333044337220460042a66608866e3c08c0084c0accc120dd3000a5eb80530103d87a80001330060060033756608a0066eb8c10c008c11c008c11400458dd7182100098210011bae3040001303c37540022c607c60766ea800458c8c8cc004004044894ccc0f80045300103d87a80001323232533303e3375e00c607e0062604a6608400297ae0133005005002303f0023042002304000130183303c301b303a375401497ae013253330393028303a3754002264646464a66608060860042600c608600e2c6eb8c104004c104008dd7181f800981d9baa00116303d0023002002163758607200260720046eb8c0dc004c0ccdd50008b181a98191baa00116302030313754602860626ea8004c0ccc0c0dd50008b180980099808004919baf3010302f37546020605e6ea8c048c0bcdd5000980699818a611e581c2be4b4dccb1e87fbb81f5e3ec518646792b1909c7add563bc162f97b004bd70181818169baa001163300c3758605e00c466ebcc038c0b4dd5180718169baa001300b3302f375201497ae03756605c605e0046eb0c0b4004c0b4c0b4c0b4c0b4008dd5981580098159815800981518150011bac30280013024375403e6eb8c098c08cdd500d8a999810980580d099191919192999813180a8008a99981498141baa021150021615333026301000115333029302837540422a0042c2c604c6ea808054ccc090c04cc094dd500089919191919192999815180c98159baa00113232533302c3016302d37540022646464a66605e603c60606ea80044c8c94ccc0c4c048c0c8dd50008991980d00089929998199811181a1baa02e1533303300115333033302000213371e6eb8c058c0d4dd50011bae30163035375401e29405280a999819800898100010a5053330323371e6eb8c05cc0d0dd50009bae30173034375401c2a6660646038603a00a2a66606460266052604c6eacc05cc0d0dd50018a9998191981298131bab30173034375400600c2a6660646604a604c6eacc05cc0d0dd5180b981a1baa00800615333032301f00e13302500a375c602a60686ea80385280a5014a029405280a503036303337540022c604260646ea8004c0d0c0c4dd50008b180a00099808803119baf301130303754602260606ea8004c038cc0c8dd480125eb80dd7181898171baa00116300e302d3754601c605a6ea8c040c0b4dd5000981798161baa001163300b3758605c008466ebcc034c0b0dd50008041bac302d302e302e302e302e302e302e0023758605800260586058002604e6ea8088c0a4c098dd50008b18141814801181380098119baa01b16374a90011b874801088c8cc00400400c894ccc094004530103d87a800013232533302430050021300b330280024bd7009980200200098148011813800918118009119198008008019129998118008a5eb804c8c94ccc088c0140084cc098008cc0100100044cc010010004c09c008c0940048c084c08800494ccc0780045300103d87a8000130023301f30200014bd701ba548000894ccc068c024c06cdd500109919191919191919191919191919191919191919191919191919191919191919299981e982000109981180d89981180c89981180b89981180a8998118098998118088998118078998118068998118058998118048998118038998118028998118018998118008a8108b181f000981f001181e000981e001181d000981d001181c000981c001181b000981b001181a000981a00118190009819001181800098180011817000981700118160009816001181500098150011814000981400118130009813001181200098120011bae30220013022002375c604000260386ea80085888c94ccc068c0240044c8c94ccc07cc0880085401058dd71810000980e1baa0031533301a30040011533301d301c37540062a0042c2c60346ea8008dc3a4004600200244a66603200229000099b8048008cc008008c07000494ccc050c94ccc054c0140045288a99980a9802180b1baa001132337106e340052040375c6034602e6ea800458c014c058dd50008a99980a1801180c980d180d180d180b1baa0011533301430023019301a301a301a301a301637540022a6660286004603260346034603460346034602c6ea800454ccc050c008c064c068c068c068c068c068c068c058dd50008a99980a1801180c980d180d180d180d180d180d180d180b1baa0011533301430023019301a301a301a301a301a301a301a301a301637540022a66602860046032603460346034603460346034603460346034602c6ea800454ccc050c008c064c068c068c068c068c068c068c068c068c068c068c058dd50008a99980a1801180c980d180d180d180d180d180d180d180d180d180d180d180b1baa0011533301430023019301a301a301a301a301a301a301a301a301a301a301a301a301637540022a6660286004603260346034603460346034603460346034603460346034603460346034602c6ea800454ccc050c008c064c068c068c068c068c068c068c068c068c068c068c068c068c068c068c068c058dd5000899299980a98028008a5115333015300430163754002264a66602c66e212000371a0022a66602c66e20dc6800a4180042a66602c66010002910107687474703a2f2f0014a22a66602c66010002910107697066733a2f2f0014a22660100029110868747470733a2f2f0014a02940dd7180d180b9baa001163019301a301a301a301a301a301a301a301a301a301a301a301a301a3016375400229405280a5014a029405280a5014a029405280a5014a0294094ccc04cc00c0045288a9998099801180a1baa00113253330143371090001b8d0011337106e3400520c00214a06eb8c060c054dd50008b1b8748000dd7a60103d87a8000230153016301600122325333011337106e3400c004528099b8f33371890000008018011b8d00122323300100100322533301400114a0264a66602466e3cdd7180b8010020a51133003003001301700123002001300100122533300f00114bd700998081806980880099801001180900098008009129998068008a4000266e012002330020023010001300837540026016601800460140026014004601000260086ea8004526136565734aae7555cf2ab9f5740ae855d11";
    private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    private static final SecureRandom RANDOM = new SecureRandom();
    public static PlutusScript connectedGoodsScript = PlutusBlueprintUtil.getPlutusScriptFromCompiledCode(CONNECTED_GOODS_CODE, PlutusVersion.v3);
    public static PlutusScript socialHubScript = PlutusBlueprintUtil.getPlutusScriptFromCompiledCode(SOCIAL_HUB_CODE, PlutusVersion.v3);

    public static String itemsToText(Map<String, String> items) {
        return items.entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .map(entry -> entry.getKey() + "," + entry.getValue())
                .collect(Collectors.joining("\n"));
    }

    public static Map<String, String> generateItems(int amount, String assetPrefix, int offset) {
        Map<String, String> items = new HashMap<>();
        for (int i = offset + 1; i <= amount + offset; i++) {
            items.put(assetPrefix + String.format("%03d", i), generatePassword());
        }
        return items;
    }

    private static String generatePassword() {
        int length = 16;
        StringBuilder stringBuilder = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            int randomIndex = RANDOM.nextInt(CHARACTERS.length());
            stringBuilder.append(CHARACTERS.charAt(randomIndex));
        }
        return stringBuilder.toString();
    }

    public static String computeCertificateHashByConnectedGoodsDatum(Map<String, String> items) {
        return applySHA_256(itemsToText(items));
    }

    public static List<String> itemsToLines(Map<String, String> items) {
        return items.entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .map(entry -> entry.getKey() + "," + entry.getValue())
                .collect(Collectors.toList());
    }

    public static String applySHA_256(String input) {
        return applyHashFunction(input.getBytes(), "SHA-256");
    }

    private static String applyHashFunction(byte[] input, String algorithm) {
        try {
            MessageDigest digest = MessageDigest.getInstance(algorithm);
            byte[] hashBytes = digest.digest(input);

            StringBuilder hexString = new StringBuilder();
            for (byte b : hashBytes) {
                String hex = String.format("%02x", b);
                hexString.append(hex);
            }
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(algorithm + " algorithm not found", e);
        }
    }

    public static String applySHA3_256(String input) {
        return applyHashFunction(input.getBytes(), "SHA3-256");
    }

    public static String applySHA3_256(byte[] input) {
        return applyHashFunction(input, "SHA3-256");
    }

    public static boolean includesConnectedGoodsToken(AddressUtxo addressUtxo) throws CborSerializationException {
        String connectedGoodsPolicyId = connectedGoodsScript.getPolicyId();
        return addressUtxo.getAmounts().stream()
                .anyMatch(asset -> asset.getPolicyId() != null && asset.getPolicyId().equals(connectedGoodsPolicyId));
    }


    public static boolean includesSocialHubToken(AddressUtxo addressUtxo) throws CborSerializationException {
        String socialHubPolicyId = socialHubScript.getPolicyId();
        return addressUtxo.getAmounts().stream()
                .anyMatch(asset -> asset.getPolicyId() != null && asset.getPolicyId().equals(socialHubPolicyId));
    }

    public static String getSocialHubTokenName(AddressUtxo addressUtxo) throws CborSerializationException {
        String socialHubPolicyId = socialHubScript.getPolicyId();
        Optional<Amt> optionalAmount = addressUtxo.getAmounts().stream()
                .filter(asset -> asset.getPolicyId() != null && asset.getPolicyId().equals(socialHubPolicyId)).findFirst();
        return optionalAmount.map(Amt::getAssetName).orElse(null);
    }
}