diff --git a/.gitignore b/.gitignore index 01d6fe89755df3f80ddde41228aa7a584eb9be72..6a807edc37cedd17d998217fc4be66905bad450b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target/ -.vscode \ No newline at end of file +.vscode +.idea \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0f1c75e78f7aed8cca2c9d66cda8fbf70954df8c..8755d419d802e896fd2eba86b760c951d9c0b828 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,11 @@ </properties> <dependencies> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.10.1</version> + </dependency> <dependency> <groupId>commons-codec</groupId> diff --git a/src/main/java/fr/miage/App.java b/src/main/java/fr/miage/App.java index 67493cc55395b87b960b1bb66d44218cab82eb15..831f4f34bf221e164863de9678721779f9e10880 100644 --- a/src/main/java/fr/miage/App.java +++ b/src/main/java/fr/miage/App.java @@ -1,6 +1,7 @@ package fr.miage; -import java.security.NoSuchAlgorithmException; +import java.util.HashSet; +import java.util.Set; /** * Hello world! @@ -8,14 +9,23 @@ import java.security.NoSuchAlgorithmException; */ public class App { - public static void main( String[] args ) throws NoSuchAlgorithmException + public static void main( String[] args ) { System.out.println("Début de la blockchain"); + // Création de 2 wallets + Wallet bobWallet = new Wallet("publicKey1", "privateKey1"); + Wallet aliceWallet = new Wallet("publicKey2", "privateKey2"); + Set<Wallet> wallets = new HashSet<Wallet>() { + { + add(bobWallet); + add(aliceWallet); + } + }; // Création de la blockchain - Blockchain blockchain = new Blockchain("BloBlockchain"); - //Création de 2 wallets - Wallet bobWallet = new Wallet("Ceci est la clé privé de bob"); - ///System.out.println(bobWallet.getPublicKey()); - //System.out.println(bobWallet.getPrivateKey()); + Blockchain blockchain = new Blockchain("BloBlockchain", wallets); + System.out.println(blockchain); + + // MinageManager minageManager = new MinageManager(blockchain); + // minageManager.mineABloc(); } } diff --git a/src/main/java/fr/miage/Bloc.java b/src/main/java/fr/miage/Bloc.java index 2fe1256d91c0e03449ad26873225eb1b610d69d0..2e47b1b45b3f072159914130454d220b8f038a3c 100644 --- a/src/main/java/fr/miage/Bloc.java +++ b/src/main/java/fr/miage/Bloc.java @@ -1,31 +1,38 @@ package fr.miage; +import java.time.Instant; +import java.util.ArrayList; import java.util.List; +import java.util.Random; +import java.util.Set; + +import org.apache.commons.codec.digest.DigestUtils; public class Bloc { private String hash; private List<Transaction> transactions; - private String header; - + private String header; // Contient le nonce et le hash du bloc précédent + + public Bloc(String hash, List<Transaction> transactions) { + this.hash = hash; + this.transactions = transactions; + } + + public String getHash() { + return hash; + } + + public String getHeader() { + return header; + } - public Bloc(String hash,List<Transaction> transactions) { + public void setHash(String hash) { this.hash = hash; - this.transactions = transactions; - } - - public String getHash() { - return hash; - } - public String getHeader() { - return header; - } - public void setHash(String hash) { - this.hash = hash; - } - public void setHeader(String header) { - this.header = header; - } + } + public void setHeader(String header) { + this.header = header; + } public Bloc(List<Transaction> transactions) { this.transactions = transactions; @@ -34,7 +41,59 @@ public class Bloc { public List<Transaction> getTransactions() { return transactions; } + public void setTransactions(List<Transaction> transactions) { this.transactions = transactions; } + + public static Bloc createGenuineBloc(Set<Wallet> wallets) { + List<Transaction> transactions = new ArrayList<Transaction>(); + for (Wallet wallet : wallets) { + int amount = RandomNumberGenerator.getRandomNumber(1, 20); + UTxO utxo = new UTxO(null, amount); + wallet.addUTxO(utxo); + List<UTxO> utxos = new ArrayList<UTxO>() { + { + add(utxo); + } + }; + + Coinbase coinbase = new Coinbase(utxos, wallet.getPublicKey(), EnumTypeTransaction.COINBASE); + transactions.add(coinbase); + } + + BlocHeader blocHeader = new BlocHeader("", Instant.now().toEpochMilli(), 0); + String header = blocHeader.toString(); + String hash = generateHash(header, transactions); + return new Bloc(hash, transactions); + } + + private static String generateHash(String header, List<Transaction> transactions) { + return DigestUtils.sha256Hex(header + transactions.toString()); + } + + public String getTransactionsString() { + StringBuilder sb = new StringBuilder(); + for (Transaction transaction : this.transactions) { + sb.append(transaction.toString()); + } + return sb.toString(); + } + + public class RandomNumberGenerator { + public static int getRandomNumber(int min, int max) { + Random random = new Random(); + return random.nextInt(max) + min; + } + } + + + @Override + public String toString() { + return "{" + + " hash='" + getHash() + "'" + + ", transactions='" + getTransactions() + "'" + + ", header='" + getHeader() + "'" + + "}"; + } } diff --git a/src/main/java/fr/miage/BlocHeader.java b/src/main/java/fr/miage/BlocHeader.java index 0f5f02ddd3adf6fe0c756a8fd20d4c8ad622c084..27e640a90eb5ee6d59eecb8e82df0681062fe624 100644 --- a/src/main/java/fr/miage/BlocHeader.java +++ b/src/main/java/fr/miage/BlocHeader.java @@ -1,19 +1,19 @@ -// package fr.miage; +package fr.miage; -// public class BlocHeader { -// private String previousHash; -// private long time; -// private long nonce; +public class BlocHeader { + private String previousHash; + private long time; + private long nonce; // private long size; // public static final int DIFFICULTY = 27; -// public BlocHeader(String hash, String previousHash, long time, long nonce, long size) { + public BlocHeader(String previousHash, long time, long nonce) { // this.hash = hash; -// this.previousHash = previousHash; -// this.time = time; -// this.nonce = nonce; + this.previousHash = previousHash; + this.time = time; + this.nonce = nonce; // this.size = size; -// } + } // public String getHash() { // return hash; @@ -23,25 +23,25 @@ // this.hash = hash; // } -// public String getPreviousHash() { -// return previousHash; -// } + public String getPreviousHash() { + return previousHash; + } // public void setPreviousHash(String previousHash) { // this.previousHash = previousHash; // } -// public long getTime() { -// return time; -// } + public long getTime() { + return time; + } // public void setTime(long time) { // this.time = time; // } -// public long getNonce() { -// return nonce; -// } + public long getNonce() { + return nonce; + } // public void setNonce(long nonce) { // this.nonce = nonce; @@ -57,4 +57,4 @@ -// } +} diff --git a/src/main/java/fr/miage/Blockchain.java b/src/main/java/fr/miage/Blockchain.java index c3f539863199bf9467fefa2b17cddad8ed9d4f9c..180bf59c037d0bafa3c79733822569aa8b100e1e 100644 --- a/src/main/java/fr/miage/Blockchain.java +++ b/src/main/java/fr/miage/Blockchain.java @@ -1,14 +1,50 @@ package fr.miage; import java.util.LinkedList; +import java.util.Set; public class Blockchain { private String name; private LinkedList<Bloc> blocs; + private Set<Wallet> wallets; - public Blockchain(String name) { + public Blockchain(String name, Set<Wallet> wallets) { this.name = name; - this.blocs = new LinkedList<Bloc>(); + this.wallets = wallets; + // Création du premier bloc contenant les transactions de coinbase + this.blocs = new LinkedList<>() { + { + add(Bloc.createGenuineBloc(wallets)); + } + }; + } + + 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 Set<Wallet> getWallets() { + return this.wallets; + } + + @Override + public String toString() { + return "{" + + " name='" + getName() + "'" + + ", blocs='" + getBlocs() + "'" + + ", wallets='" + getWallets() + "'" + + "}"; } - } diff --git a/src/main/java/fr/miage/Coinbase.java b/src/main/java/fr/miage/Coinbase.java new file mode 100644 index 0000000000000000000000000000000000000000..1f2ab8f52d96cfa579f17aafe5ff5f99e8b113ed --- /dev/null +++ b/src/main/java/fr/miage/Coinbase.java @@ -0,0 +1,14 @@ +package fr.miage; + +import java.util.ArrayList; +import java.util.List; + +public class Coinbase extends Transaction { + public Coinbase(List<UTxO> inputs, List<UTxO> outputs, String signature, EnumTypeTransaction typeTransaction) { + super(inputs, outputs, signature, EnumTypeTransaction.COINBASE); + } + + public Coinbase(List<UTxO> outputs, String signature, EnumTypeTransaction typeTransaction) { + super(new ArrayList<UTxO>(), outputs, signature, EnumTypeTransaction.COINBASE); + } +} diff --git a/src/main/java/fr/miage/MinageManager.java b/src/main/java/fr/miage/MinageManager.java index ac0d41424d0e514939c3fab38bb5c004f9f4cfd1..f541fe8f89d28848548052542c8bebdb4ee1f485 100644 --- a/src/main/java/fr/miage/MinageManager.java +++ b/src/main/java/fr/miage/MinageManager.java @@ -1,39 +1,45 @@ package fr.miage; import java.math.BigInteger; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.Optional; import java.util.stream.DoubleStream; -import javax.swing.text.html.Option; - import org.apache.commons.codec.digest.DigestUtils; public class MinageManager { private static final int DIFFICULTY = 27; + private Blockchain blockchain; - - - public MinageManager() { + public MinageManager(Blockchain blockchain) { + this.blockchain = blockchain; } + public Bloc mineABloc() { + Bloc previousBloc = blockchain.getLastBloc(); + return mineABloc(previousBloc); + } public Bloc mineABloc(Bloc previousBloc) { - double target = Math.pow(2, 256 - DIFFICULTY); - //DoubleStream doubleStream = DoubleStream.iterate(0, i -> i + 1).limit((long) Math.pow(2, DIFFICULTY)); - Compute result = new Compute(new BigInteger(DigestUtils.sha256Hex(previousBloc.getHeader() + Math.pow(2,DIFFICULTY)), 16).doubleValue(), Math.pow(2,DIFFICULTY)); - if(result.hash < target){ - System.out.println("Bloc miné avec succès : " + result.nonce); - System.out.println("Hash : " + DigestUtils.sha256Hex(previousBloc.getHeader() + result.nonce)); - Bloc bloc = new Bloc(transactions, new BlocHeader(DigestUtils.sha256Hex(previousBloc.getHeader() + result.nonce), previousBloc.getHeader(), System.currentTimeMillis(), result.nonce, transactions.size())); - } - System.out.println("Failed after " + BlocHeader.DIFFICULTY + " tries"); - return new Result("", BlocHeader.DIFFICULTY); + String header = previousBloc.getHeader(); + double target = computeTarget(header); + int nonce = findNonce(header, target); + String hash = DigestUtils.sha256Hex(header + nonce + previousBloc.getTransactionsString()); + return new Bloc(hash, previousBloc.getTransactions()); + } + private static double computeTarget(String header) { + return new BigInteger(header, 16).doubleValue() / Math.pow(2, DIFFICULTY); } - record Result(String hash, double nonce){} - record Compute(double hash, double nonce){} + private static int findNonce(String header, double target) { + return (int) DoubleStream.iterate(0, i -> i + 1).limit((long) Math.pow(2, DIFFICULTY)) + .filter(nonce -> new BigInteger(DigestUtils.sha256Hex(header + nonce), 16).doubleValue() < target) + .findFirst().orElseThrow(); + } + + record Result(String hash, double nonce) { + } + + record Compute(double hash, double nonce) { + } } diff --git a/src/main/java/fr/miage/Transaction.java b/src/main/java/fr/miage/Transaction.java index 96f1f2cca9ee346433479e34626a9b12034b00f6..6c6e7b5f26dd2e605de16aa4c3c2d58ad50d7075 100644 --- a/src/main/java/fr/miage/Transaction.java +++ b/src/main/java/fr/miage/Transaction.java @@ -1,36 +1,53 @@ package fr.miage; import java.util.List; +import java.util.UUID; public class Transaction { - private String id; + private UUID id; private List<UTxO> inputs; private List<UTxO> outputs; private String signature; - private String typeTransaction; - private Bloc bloc; + private EnumTypeTransaction typeTransaction; - public Transaction(String id, List<UTxO> inputs, List<UTxO> outputs, String signature, String typeTransaction) { - this.id = id; + public Transaction(List<UTxO> inputs, List<UTxO> outputs, String signature, EnumTypeTransaction typeTransaction) { this.inputs = inputs; this.outputs = outputs; this.signature = signature; } - public void createTransaction(Wallet fromWallet, String toWallet, double amount, List<UTxO> utxoInput, List<UTxO> utxoOutput){ - //créer transaction normale - this.signature = fromWallet.getPrivateKey(); + public UUID getId() { + return this.id; + } + public void setId(UUID id) { + this.id = id; } - public void createFirstTranscation(Wallet fromWallet, String toWallet, double amount, List<UTxO> utxoInput, List<UTxO> utxoOutput){ - //créer transaction normale - this.signature = fromWallet.getPrivateKey(); - utxoInput.add(new UTxO(fromWallet, null, amount)) - + public List<UTxO> getInputs() { + return this.inputs; } - //créer transaction coinbase + public List<UTxO> getOutputs() { + return this.outputs; + } - //un bloc avec que des coinbase avec un seul utxo de sortie + public String getSignature() { + return this.signature; + } + + public EnumTypeTransaction getTypeTransaction() { + return this.typeTransaction; + } + + @Override + public String toString() { + return "{" + + " id='" + getId() + "'" + + ", inputs='" + getInputs() + "'" + + ", outputs='" + getOutputs() + "'" + + ", signature='" + getSignature() + "'" + + ", typeTransaction='" + getTypeTransaction() + "'" + + "}"; + } } diff --git a/src/main/java/fr/miage/UTxO.java b/src/main/java/fr/miage/UTxO.java index cc7d731bc182f53ef981f53027c8407739f81a29..d1510884f4d98ac4c6e74b0098d6aafd594ca6d6 100644 --- a/src/main/java/fr/miage/UTxO.java +++ b/src/main/java/fr/miage/UTxO.java @@ -1,13 +1,31 @@ package fr.miage; +import java.util.UUID; + public class UTxO { //UTXO : Hash qui renvoie a la transaction d'avant - private Wallet hashWallet; // hash du wallet + private UUID uuid; + private Transaction transaction; // transaction précédente (provenance de l'argent) --> peut etre meme que le hash de la transaction private double montant; - public UTxO(Wallet hashWallet, Transaction transaction, double montant) { - this.hashWallet = hashWallet; + public UTxO(Transaction transaction, double montant) { + this.transaction = transaction; this.montant = montant; } - + + public Transaction getTransaction() { + return this.transaction; + } + + public double getMontant() { + return this.montant; + } + + @Override + public String toString() { + return "{" + + "transaction='" + getTransaction() + "'" + + ", montant='" + getMontant() + "'" + + "}"; + } } diff --git a/src/main/java/fr/miage/Wallet.java b/src/main/java/fr/miage/Wallet.java index 53bdc4bf4330f564fc7d2f7647c1acbfcb961771..cf65318e644f88d4931f63b6041a41c062165c12 100644 --- a/src/main/java/fr/miage/Wallet.java +++ b/src/main/java/fr/miage/Wallet.java @@ -3,39 +3,53 @@ package fr.miage; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.List; public class Wallet { - private String publicKey; private String privateKey; // pour la signature private double solde; private List<UTxO> utxos; - - public Wallet(String privateKey) throws NoSuchAlgorithmException { - this.init(privateKey); + public Wallet(String publicKey, String privateKey) { + this.publicKey = publicKey; + this.privateKey = privateKey; + this.utxos = new ArrayList<>(); } + public void initWallet(byte[] data) throws NoSuchAlgorithmException { - private void init(String privateKey) throws NoSuchAlgorithmException{ + } + + private String hashSha256(String privateKey) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] hash = md.digest(privateKey.getBytes(StandardCharsets.UTF_8)); - StringBuilder hashedByte = new StringBuilder(); - for(byte b : hash){ - hashedByte.append(String.format("%02x", b)); - } - this.publicKey = hashedByte.toString(); - this.privateKey = privateKey; - this.solde = 20; - this.utxos.add(new UTxO(this, null, solde)) + return new String(hash, StandardCharsets.UTF_8); + } + + public String getPublicKey() { + return this.publicKey; } public String getPrivateKey() { - return privateKey; + return this.privateKey; } - public String getPublicKey() { - return publicKey; + + public List<UTxO> getUtxos() { + return this.utxos; + } + + public void addUTxO(UTxO utxo) { + this.utxos.add(utxo); } + @Override + public String toString() { + return "{" + + " publicKey='" + getPublicKey() + "'" + + ", privateKey='" + getPrivateKey() + "'" + + ", utxos='" + getUtxos() + "'" + + "}"; + } }