Skip to content
Snippets Groups Projects
Blockchain.java 5.32 KiB
package fr.miage;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;

import fr.miage.blocs.Bloc;

public class Blockchain {

    private static Blockchain instance = null;
    private String name;
    private LinkedList<Bloc> blocs;
    private List<Wallet> wallets;
    public static Set<UTxO> utxos = new HashSet<>();
    public static Set<Transaction> transactionsPool = new HashSet<>();

    private Blockchain(String name) throws NoSuchAlgorithmException, InterruptedException {
        this.name = name;
        this.wallets = setupWallet();
        // Création du premier bloc contenant les transactions de coinbase
        this.blocs = new LinkedList<>() {
            {
                add(Bloc.createGenuineBloc(wallets));
            }
        };
        ;
    }

    public static Blockchain getInstance(String name) throws NoSuchAlgorithmException, InterruptedException {
        if (instance == null) {
            instance = new Blockchain(name);
        }
        return instance;

    }

    public List<Wallet> setupWallet() throws NoSuchAlgorithmException, InterruptedException {
        List<Wallet> wallets = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            wallets.add(new Wallet("Wallet" + i));
        }
        return wallets;
    }

    // Permet de créer une transaction avec deux wallets aléatoire et de l'ajouter
    // dans la transaction pool;
    public void createTransaction() throws InterruptedException {
        List<Wallet> walletsCopy = new ArrayList<>();
        walletsCopy.addAll(wallets);
        Wallet walletSender = selectWallet(walletsCopy);
        walletsCopy.remove(walletSender);
        Wallet walletReceiver = selectWallet(walletsCopy);
        walletsCopy.remove(walletReceiver);
        double amountWalletSender = walletSender.getUtxos().stream().mapToDouble(utxo -> utxo.getMontant()).sum();
        double randomAmountToSend = new Random().nextDouble(amountWalletSender);
        if (walletSender.getUtxos().isEmpty() || walletReceiver.getUtxos().isEmpty()) {
            System.out.println("Wallets vides");
            return;
        }
        System.out.println("[TRANSACTION] --- Création d'une transaction =========================================\n");
        System.out.println("            " + walletSender.getName() + " envoie " + randomAmountToSend + " à "
                + walletReceiver.getName() + "\n");

        Transaction tx = new Transaction(walletSender, walletReceiver, EnumTypeTransaction.NORMAL);
        tx.putUTxO(walletSender, walletReceiver, randomAmountToSend);

        Blockchain.utxos.addAll(tx.getOutputs());
        Blockchain.utxos.removeAll(tx.getInputs());
        String transactionData = getTransactionData(tx);

        try {
            if (makeFakeSign()) {
                Wallet fakeWallet = new Wallet("FakeWallet");
                tx.sign(transactionData, fakeWallet.getKeyPair().getPrivate());
            } else {
                tx.sign(transactionData, walletSender.getKeyPair().getPrivate());
            }
            System.out.println("[SIGNATURE] --- Signature de la transaction : " + tx.getSignature());
            if (tx.verifySignature(transactionData, walletSender.getKeyPair().getPublic())) {
                Blockchain.transactionsPool.add(tx);
            } else {
                System.out.println("[SIGNATURE] --- Signature non valide : TRANSACTION REJETEE");
                Blockchain.utxos.removeAll(tx.getOutputs());
                Blockchain.utxos.addAll(tx.getInputs());
                return;
            }
        } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
            e.printStackTrace();
        }

    }

    public String getTransactionData(Transaction tx) {
        return tx.getInputs().toString() + tx.getOutputs().toString() + tx.getId();
    }

    public boolean makeFakeSign() {
        int num1 = (int) (Math.random() * 5) + 1;
        int num2 = (int) (Math.random() * 5) + 1;
        return num1 == num2;
    }

    public Wallet selectWallet(List<Wallet> wallets) {
        if (wallets.size() == 0) {
            return null;
        }
        if (wallets.size() == 1) {
            return wallets.get(0);
        }
        int index = new Random().nextInt(wallets.size());

        int i = 0;
        for (Wallet wallet : wallets) {
            if (i == index) {
                return wallet;
            }
            i++;
        }
        return null;
    }

    public void addBloc(Bloc bloc) {
        this.blocs.add(bloc);
    }

    public Bloc getLastBloc() {
        return this.blocs.getLast();
    }

    public String getName() {
        return this.name;
    }

    private LinkedList<Bloc> getBlocs() {
        return this.blocs;
    }

    public List<Wallet> getWallets() {
        return this.wallets;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i < this.blocs.size(); i++) {
            sb.append("Bloc n° : " + i + this.blocs.get(i).getHash() + "\n");
        }
        return sb.toString();
    }

}