Initial commit

This commit is contained in:
Aelita4 2023-01-04 22:15:07 +01:00
commit 8666ad8752
11 changed files with 485 additions and 0 deletions

31
.gitignore vendored Normal file
View File

@ -0,0 +1,31 @@
### IntelliJ IDEA ###
out/
!**/src/main/**/out/
!**/src/test/**/out/
target/
.idea/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

15
DzieCoin.iml Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_19">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

16
pom.xml Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<artifactId>DzieCoin</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>19</maven.compiler.source>
<maven.compiler.target>19</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@ -0,0 +1,118 @@
package pl.mikorosa.dziecoin;
import java.util.ArrayList;
import java.util.List;
public class Block {
private String hash;
private String prevHash;
private int height;
private List<Transaction> transactions;
private List<NFT> nfts;
private int nonce;
public Block() {
this.transactions = new ArrayList<>();
this.nfts = new ArrayList<>();
}
public Block(String prevHash, int height, List<Transaction> transactions) {
this.prevHash = prevHash;
this.height = height;
this.transactions = transactions;
this.nfts = new ArrayList<>();
}
public Block(String hash, String prevHash, int height, List<Transaction> transactions, List<NFT> nfts, int nonce) {
this.hash = hash;
this.prevHash = prevHash;
this.height = height;
this.transactions = transactions;
this.nfts = nfts;
this.nonce = nonce;
}
public void mineBlock() {
String blockStruct = this.prevHash + this.height;
for (Transaction transaction : this.transactions) {
blockStruct += transaction.toString();
}
for (NFT nft : nfts) {
blockStruct += nft.toString();
}
int nonce = 0;
String hash;
System.out.println();
while(true) {
hash = CalculateHash.sha256sum(blockStruct + nonce);
if(hash.startsWith("0".repeat(Main.difficulty))) break;
nonce++;
System.out.print("\rMining block " + height + "... " + nonce);
}
System.out.println("\nSuccessfully mined block " + height + " (nonce: " + nonce + ")");
this.hash = hash;
this.nonce = nonce;
}
public void addTransaction(Transaction transaction) {
transactions.add(transaction);
}
public void addNFT(NFT nft) {
nfts.add(nft);
}
public enum VerifyCodes {
SUCCESS,
INVALID_POW_SIGNATURE,
HASH_MISMATCH,
INVALID_BLOCK_HASH
}
public VerifyCodes verifyBlock() {
String blockStruct = this.prevHash + this.height;
for (Transaction transaction : this.transactions) {
blockStruct += transaction.toString();
}
for (NFT nft : nfts) {
blockStruct += nft.toString();
}
blockStruct += this.nonce;
String calculatedHash = CalculateHash.sha256sum(blockStruct);
if(!calculatedHash.equals(this.hash)) return VerifyCodes.INVALID_BLOCK_HASH;
if(!calculatedHash.startsWith("0".repeat(Main.difficulty))) return VerifyCodes.INVALID_POW_SIGNATURE;
return VerifyCodes.SUCCESS;
}
public List<Transaction> getTransactions() {
return transactions;
}
public int getHeight() {
return height;
}
public List<NFT> getNFTs() {
return nfts;
}
public String getHash() {
return hash;
}
public String getPrevHash() {
return prevHash;
}
public int getNonce() {
return nonce;
}
}

View File

@ -0,0 +1,86 @@
package pl.mikorosa.dziecoin;
import java.util.ArrayList;
import java.util.List;
public class Blockchain {
private List<Block> blocks;
private int length;
public Blockchain(String genesisBlockRecipient) {
blocks = new ArrayList<>();
length = blocks.size();
Transaction firstTransaction = new Transaction("0", genesisBlockRecipient, 10);
List<Transaction> transactions = new ArrayList<>();
transactions.add(firstTransaction);
Block genesisBlock = new Block("0", 1, transactions);
genesisBlock.mineBlock();
if(genesisBlock.verifyBlock() != Block.VerifyCodes.SUCCESS) throw new IllegalStateException("Genesis block is invalid");
try {
addBlock(genesisBlock);
} catch (BlockchainIntegrityException e) {
System.out.println("Blockchain Integrity Exception: " + e.getMessage());
}
}
public int getLength() {
return length;
}
public List<Block> getBlocks() {
return blocks;
}
public Block getLatestBlock() {
return blocks.get(length - 1);
}
public Block.VerifyCodes validateChain() {
for (int i = 1; i < (length - 1); i++) {
if(blocks.get(i).verifyBlock() != Block.VerifyCodes.SUCCESS) return blocks.get(i).verifyBlock();
if(!blocks.get(i).getHash().equals(blocks.get(i + 1).getPrevHash())) return Block.VerifyCodes.HASH_MISMATCH;
}
for (Block block : blocks) {
if(!block.getHash().startsWith("0".repeat(Main.difficulty))) return Block.VerifyCodes.INVALID_POW_SIGNATURE;
}
return Block.VerifyCodes.SUCCESS;
}
public void addBlock(Block block) throws BlockchainIntegrityException {
Block.VerifyCodes status = block.verifyBlock();
if(status == Block.VerifyCodes.HASH_MISMATCH) throw new BlockchainIntegrityException("Block " + block.getHeight() + " calculated hash and actual hash does not match");
if(status == Block.VerifyCodes.INVALID_POW_SIGNATURE) throw new BlockchainIntegrityException("Block " + block.getHeight() + " contains invalid proof of work signature");
if(status == Block.VerifyCodes.INVALID_BLOCK_HASH) throw new BlockchainIntegrityException("Block " + block.getHeight() + " contains invalid hash");
if(length != 0) { // don't check for very first block
if (!getLatestBlock().getHash().equals(block.getPrevHash())) throw new BlockchainIntegrityException("Block " + block.getHeight() + " previous hash does not match");
for (Transaction transaction : block.getTransactions()) {
if(this.getAddressBalance(transaction.getSender()) < transaction.getAmount())
throw new BlockchainIntegrityException("Wallet " + transaction.getSender() + " at block " + block.getHeight() + " tried to spend too much\nBalance: " + this.getAddressBalance(transaction.getSender()) + "\nTried to spend: " + transaction.getAmount());
}
}
blocks.add(block);
length++;
}
public int getAddressBalance(String walletAddress) {
int amount = 0;
for (Block block : blocks) {
for (Transaction transaction : block.getTransactions()) {
if(transaction.getSender().equals(walletAddress)) amount -= transaction.getAmount();
if(transaction.getRecipient().equals(walletAddress)) amount += transaction.getAmount();
}
}
return amount;
}
}

View File

@ -0,0 +1,8 @@
package pl.mikorosa.dziecoin;
public class BlockchainIntegrityException extends Exception {
public BlockchainIntegrityException(String message) {
super(message);
}
}

View File

@ -0,0 +1,45 @@
package pl.mikorosa.dziecoin;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public abstract class CalculateHash {
public static String sha256sum(String input) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
} catch(NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
byte[] sha256sum = md.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for(byte b : sha256sum) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public static String md5sum(String input) {
MessageDigest md;
try {
md = MessageDigest.getInstance("MD5");
} catch(NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
byte[] md5sum = md.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for(byte b : md5sum) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}

View File

@ -0,0 +1,60 @@
package pl.mikorosa.dziecoin;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static int difficulty;
public static Blockchain blockchain;
public static void main(String[] args) {
Wallet w1 = new Wallet();
Wallet w2 = new Wallet();
Wallet w3 = new Wallet();
difficulty = 4;
blockchain = new Blockchain(w1.getAddress());
NFT n1 = new NFT(w1.getAddress(), "licencja na wozek widlowy");
List<Transaction> t1 = new ArrayList<>();
t1.add(new Transaction(w1.getAddress(), w2.getAddress(), 5));
Block b1 = new Block(blockchain.getLatestBlock().getHash(), blockchain.getLength() + 1, t1);
b1.addNFT(n1);
b1.mineBlock();
try {
blockchain.addBlock(b1);
} catch (BlockchainIntegrityException e) {
System.out.println("Blockchain Integrity Exception: " + e.getMessage());
}
List<Transaction> t2 = new ArrayList<>();
t2.add(new Transaction(w1.getAddress(), w3.getAddress(), 2));
t2.add(new Transaction(w2.getAddress(), w3.getAddress(), 3));
Block b2 = new Block(blockchain.getLatestBlock().getHash(), blockchain.getLength() + 1, t2);
b2.mineBlock();
try {
blockchain.addBlock(b2);
} catch (BlockchainIntegrityException e) {
System.out.println("Blockchain Integrity Exception: " + e.getMessage());
}
for (Block block : blockchain.getBlocks()) {
System.out.println("Block " + block.getHeight() + ": " + block.getHash());
for (Transaction transaction : block.getTransactions()) {
System.out.println(" " + transaction.getSender() + " => " + transaction.getRecipient() + " - " + transaction.getAmount());
}
}
System.out.println("Balances:");
System.out.println("Wallet 1: " + blockchain.getAddressBalance(w1.getAddress()));
System.out.println("Wallet 2: " + blockchain.getAddressBalance(w2.getAddress()));
System.out.println("Wallet 3: " + blockchain.getAddressBalance(w3.getAddress()));
}
}

View File

@ -0,0 +1,42 @@
package pl.mikorosa.dziecoin;
public class NFT {
private String contract;
private String owner;
private String data;
public NFT(String owner, String data) {
this.owner = owner;
this.data = data;
this.contract = CalculateHash.md5sum(owner + data);
}
public NFT(String contract, String owner, String data) {
String testHash = CalculateHash.md5sum(owner + data);
if(!testHash.equals(contract)) throw new IllegalStateException("NFT contract and data does not match");
this.owner = owner;
this.data = data;
this.contract = contract;
};
public String getContract() {
return contract;
}
public String getOwner() {
return owner;
}
public String getData() {
return data;
}
@Override
public String toString() {
return "NFT{" +
"contract='" + contract + '\'' +
", owner='" + owner + '\'' +
", data='" + data + '\'' +
'}';
}
}

View File

@ -0,0 +1,34 @@
package pl.mikorosa.dziecoin;
public class Transaction {
private String sender;
private String recipient;
private int amount;
public Transaction(String sender, String recipient, int amount) {
this.sender = sender;
this.recipient = recipient;
this.amount = amount;
}
public String getSender() {
return sender;
}
public String getRecipient() {
return recipient;
}
public int getAmount() {
return amount;
}
@Override
public String toString() {
return "Transaction{" +
"sender='" + sender + '\'' +
", recipient='" + recipient + '\'' +
", amount=" + amount +
'}';
}
}

View File

@ -0,0 +1,30 @@
package pl.mikorosa.dziecoin;
import java.security.*;
public class Wallet {
private String address;
private PublicKey publicKey;
private PrivateKey privateKey;
Wallet() {
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair keyPair = keyGen.genKeyPair();
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();
address = CalculateHash.md5sum(publicKey.getEncoded().toString());
} catch (NoSuchAlgorithmException e) {
System.out.println("No such algo: " + e);
} catch (ProviderException e) {
System.out.println("Provider exception: " + e);
} catch (Exception e) {
System.out.println("General exception: " + e);
}
}
public String getAddress() {
return address;
}
}