Add import/export functionality
This commit is contained in:
		
							parent
							
								
									9a72fb8f77
								
							
						
					
					
						commit
						1d574b3ee1
					
				| 
						 | 
				
			
			@ -13,5 +13,6 @@
 | 
			
		|||
    <orderEntry type="sourceFolder" forTests="false" />
 | 
			
		||||
    <orderEntry type="library" name="mysql.connector.java" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="Maven: mysql:mysql-connector-java:8.0.30" level="project" />
 | 
			
		||||
    <orderEntry type="library" name="Maven: com.github.davidmoten:commons-csv:1.6.002" level="project" />
 | 
			
		||||
  </component>
 | 
			
		||||
</module>
 | 
			
		||||
							
								
								
									
										5
									
								
								pom.xml
								
								
								
								
							
							
						
						
									
										5
									
								
								pom.xml
								
								
								
								
							| 
						 | 
				
			
			@ -20,5 +20,10 @@
 | 
			
		|||
            <artifactId>mysql-connector-java</artifactId>
 | 
			
		||||
            <version>8.0.30</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.github.davidmoten</groupId>
 | 
			
		||||
            <artifactId>commons-csv</artifactId>
 | 
			
		||||
            <version>1.6.002</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
</project>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,13 +1,35 @@
 | 
			
		|||
package pl.mikorosa.dziecoin;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.csv.CSVFormat;
 | 
			
		||||
import org.apache.commons.csv.CSVPrinter;
 | 
			
		||||
import org.apache.commons.csv.CSVRecord;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.Reader;
 | 
			
		||||
import java.io.Writer;
 | 
			
		||||
import java.nio.file.Files;
 | 
			
		||||
import java.nio.file.Paths;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
public class Blockchain {
 | 
			
		||||
    private List<Block> blocks;
 | 
			
		||||
    private int length;
 | 
			
		||||
    private boolean importToDatabase;
 | 
			
		||||
 | 
			
		||||
    public Blockchain(String genesisBlockRecipient) {
 | 
			
		||||
    public static void clearAllTablesInDatabase() {
 | 
			
		||||
        Main.db.getTransactionsTable().cleanTable();
 | 
			
		||||
        Main.db.getNFTsTable().cleanTable();
 | 
			
		||||
        Main.db.getBlocksTable().cleanTable();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Blockchain(String genesisBlockRecipient, boolean importToDatabase) {
 | 
			
		||||
        System.out.println("Automatic import to database " + (importToDatabase ? "enabled" : "disabled"));
 | 
			
		||||
        if(importToDatabase) clearAllTablesInDatabase();
 | 
			
		||||
 | 
			
		||||
        this.importToDatabase = importToDatabase;
 | 
			
		||||
        blocks = new ArrayList<>();
 | 
			
		||||
        length = blocks.size();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +49,205 @@ public class Blockchain {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Blockchain(String blocksCSVPath, String transactionCSVPath, String NFTsCSVPath, boolean importToDatabase) {
 | 
			
		||||
        System.out.println("Automatic import to database " + (importToDatabase ? "enabled" : "disabled"));
 | 
			
		||||
        if(importToDatabase) clearAllTablesInDatabase();
 | 
			
		||||
        this.importToDatabase = importToDatabase;
 | 
			
		||||
        this.blocks = new ArrayList<>();
 | 
			
		||||
        this.length = this.blocks.size();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            List<Block> toMine = importData(blocksCSVPath, transactionCSVPath, NFTsCSVPath, importToDatabase);
 | 
			
		||||
            for (Block block : toMine) {
 | 
			
		||||
                this.addBlock(block);
 | 
			
		||||
            }
 | 
			
		||||
            System.out.println("Import complete");
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            System.out.println("Error while importing data: ");
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            JOptionPane.showMessageDialog(null, "Unable to import: " + e.getMessage(), "Fatal error", JOptionPane.ERROR_MESSAGE);
 | 
			
		||||
            System.exit(1);
 | 
			
		||||
        } catch (BlockchainIntegrityException e) {
 | 
			
		||||
            JOptionPane.showMessageDialog(null, "Unable to add a block: " + e.getMessage(), "Fatal error", JOptionPane.ERROR_MESSAGE);
 | 
			
		||||
            System.exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Blockchain(boolean importToDatabase) {
 | 
			
		||||
        this.blocks = new ArrayList<>();
 | 
			
		||||
        this.length = this.blocks.size();
 | 
			
		||||
        this.importToDatabase = importToDatabase;
 | 
			
		||||
 | 
			
		||||
        this.importFromDatabase();
 | 
			
		||||
        System.out.println("Import complete");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void importFromDatabase() {
 | 
			
		||||
        System.out.println("Importing from database...");
 | 
			
		||||
        List<Block> blocksToImport = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        System.out.println("Importing block data...");
 | 
			
		||||
        List<Map<String, Object>> blocks = Main.db.getBlocksTable().select();
 | 
			
		||||
        for (Map<String, Object> block : blocks) {
 | 
			
		||||
            String hash = (String) block.get("hash");
 | 
			
		||||
            String prevHash = (String) block.get("prev_hash");
 | 
			
		||||
            int nonce = (Integer) block.get("nonce");
 | 
			
		||||
            int height = (Integer) block.get("height");
 | 
			
		||||
 | 
			
		||||
            blocksToImport.add(new Block(hash, prevHash, height, new ArrayList<>(), new ArrayList<>(), nonce));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        System.out.println("Importing transaction data...");
 | 
			
		||||
        List<Map<String, Object>> transactions = Main.db.getTransactionsTable().select();
 | 
			
		||||
        for (Map<String, Object> transaction : transactions) {
 | 
			
		||||
            String sender = (String) transaction.get("sender");
 | 
			
		||||
            String recipient = (String) transaction.get("recipient");
 | 
			
		||||
            int amount = (Integer) transaction.get("amount");
 | 
			
		||||
            int blockHeight = (Integer) transaction.get("block_height");
 | 
			
		||||
 | 
			
		||||
            blocksToImport.get(blockHeight - 1).addTransaction(new Transaction(sender, recipient, amount));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        System.out.println("Importing NFT data...");
 | 
			
		||||
        List<Map<String, Object>> NFTs = Main.db.getNFTsTable().select();
 | 
			
		||||
        for (Map<String, Object> NFT : NFTs) {
 | 
			
		||||
            String contract = (String) NFT.get("contract");
 | 
			
		||||
            String owner = (String) NFT.get("owner");
 | 
			
		||||
            String data = (String) NFT.get("data");
 | 
			
		||||
            int blockHeight = (Integer) NFT.get("block_height");
 | 
			
		||||
 | 
			
		||||
            blocksToImport.get(blockHeight - 1).addNFT(new NFT(contract, owner, data));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (Block block : blocksToImport) {
 | 
			
		||||
            try {
 | 
			
		||||
                this.addBlock(block);
 | 
			
		||||
            } catch (BlockchainIntegrityException e) {
 | 
			
		||||
                JOptionPane.showMessageDialog(null, "Unable to add a block: " + e.getMessage(), "Fatal error", JOptionPane.ERROR_MESSAGE);
 | 
			
		||||
                System.exit(1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<Block> importData(String blocksPath, String transactionsPath, String NFTsPath, boolean importToDatabase) throws IOException {
 | 
			
		||||
        List<Block> blocks = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        //if(importToDatabase) clearAllTablesInDatabase();
 | 
			
		||||
 | 
			
		||||
        Reader blocksReader = Files.newBufferedReader(Paths.get(blocksPath));
 | 
			
		||||
        Iterable<CSVRecord> blocksRecords = CSVFormat.DEFAULT.withHeader("height", "hash", "prev_hash", "nonce").withFirstRecordAsHeader().parse(blocksReader);
 | 
			
		||||
        System.out.println("Importing block data...");
 | 
			
		||||
        for(CSVRecord record : blocksRecords) {
 | 
			
		||||
            int height = Integer.parseInt(record.get("height"));
 | 
			
		||||
            String hash = record.get("hash");
 | 
			
		||||
            if(!hash.startsWith("0".repeat(Main.difficulty))) {
 | 
			
		||||
                JOptionPane.showMessageDialog(null, "Unable to import:\nImport data does not match difficulty (" + Main.difficulty + ")", "Fatal error", JOptionPane.ERROR_MESSAGE);
 | 
			
		||||
                System.exit(1);
 | 
			
		||||
            }
 | 
			
		||||
            String prevHash = record.get("prev_hash");
 | 
			
		||||
            int nonce = Integer.parseInt(record.get("nonce"));
 | 
			
		||||
            Block block = new Block(hash, prevHash, height, new ArrayList<>(), new ArrayList<>(), nonce);
 | 
			
		||||
            blocks.add(block);
 | 
			
		||||
            //if(importToDatabase) Main.db.getBlocksTable().insert(height, hash, prevHash, nonce);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Reader transactionsReader = Files.newBufferedReader(Paths.get(transactionsPath));
 | 
			
		||||
        Iterable<CSVRecord> transactionRecords = CSVFormat.DEFAULT.withHeader("id", "sender", "recipient", "amount", "block_height").withFirstRecordAsHeader().parse(transactionsReader);
 | 
			
		||||
        System.out.println("Importing transaction data...");
 | 
			
		||||
        for(CSVRecord record : transactionRecords) {
 | 
			
		||||
            String sender = record.get("sender");
 | 
			
		||||
            String recipient = record.get("recipient");
 | 
			
		||||
            int amount = Integer.parseInt(record.get("amount"));
 | 
			
		||||
            int blockHeight = Integer.parseInt(record.get("block_height"));
 | 
			
		||||
 | 
			
		||||
            blocks.get(blockHeight - 1).addTransaction(new Transaction(sender, recipient, amount));
 | 
			
		||||
            //if(importToDatabase) Main.db.getTransactionsTable().insert(sender, recipient, amount, blockHeight);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Reader NFTsReader = Files.newBufferedReader(Paths.get(NFTsPath));
 | 
			
		||||
        Iterable<CSVRecord> NFTsRecords = CSVFormat.DEFAULT.withHeader("id", "contract", "owner", "data", "block_height").withFirstRecordAsHeader().parse(NFTsReader);
 | 
			
		||||
        System.out.println("Importing NFT data...");
 | 
			
		||||
        for(CSVRecord record : NFTsRecords) {
 | 
			
		||||
            String contract = record.get("contract");
 | 
			
		||||
            String owner = record.get("owner");
 | 
			
		||||
            String data = record.get("data");
 | 
			
		||||
            int blockHeight = Integer.parseInt(record.get("block_height"));
 | 
			
		||||
 | 
			
		||||
            blocks.get(blockHeight - 1).addNFT(new NFT(contract, owner, data));
 | 
			
		||||
            //if(importToDatabase) Main.db.getNFTsTable().insert(contract, owner, data, blockHeight);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return blocks;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum ExportType {
 | 
			
		||||
        BLOCK(1),
 | 
			
		||||
        TRANSACTION(2),
 | 
			
		||||
        NFT(4);
 | 
			
		||||
 | 
			
		||||
        public final int value;
 | 
			
		||||
 | 
			
		||||
        ExportType(int value) {
 | 
			
		||||
            this.value = value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void exportData(int bitfield) throws IOException {
 | 
			
		||||
        if((bitfield & ExportType.BLOCK.value) != 0) exportBlockData();
 | 
			
		||||
        if((bitfield & ExportType.TRANSACTION.value) != 0) exportTransactionData();
 | 
			
		||||
        if((bitfield & ExportType.NFT.value) != 0) exportNFTData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void exportBlockData() throws IOException {
 | 
			
		||||
        Writer blockWriter = Files.newBufferedWriter(Paths.get("blocks.csv"));
 | 
			
		||||
        CSVPrinter blockCsvPrinter = new CSVPrinter(blockWriter, CSVFormat.DEFAULT.withHeader("height", "hash", "prev_hash", "nonce"));
 | 
			
		||||
        for (Block block : this.blocks) {
 | 
			
		||||
            List<String> data = new ArrayList<>();
 | 
			
		||||
            data.add(Integer.toString(block.getHeight()));
 | 
			
		||||
            data.add(block.getHash());
 | 
			
		||||
            data.add(block.getPrevHash());
 | 
			
		||||
            data.add(Integer.toString(block.getNonce()));
 | 
			
		||||
            blockCsvPrinter.printRecord(data);
 | 
			
		||||
        }
 | 
			
		||||
        blockCsvPrinter.flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void exportTransactionData() throws IOException {
 | 
			
		||||
        Writer transactionWriter = Files.newBufferedWriter(Paths.get("transactions.csv"));
 | 
			
		||||
        CSVPrinter transactionCsvPrinter = new CSVPrinter(transactionWriter, CSVFormat.DEFAULT.withHeader("id", "sender", "recipient", "amount", "block_height"));
 | 
			
		||||
        int transactionCount = 1;
 | 
			
		||||
        for (Block block : this.blocks) {
 | 
			
		||||
            for (Transaction transaction : block.getTransactions()) {
 | 
			
		||||
                List<String> data = new ArrayList<>();
 | 
			
		||||
                data.add(Integer.toString(transactionCount++));
 | 
			
		||||
                data.add(transaction.getSender());
 | 
			
		||||
                data.add(transaction.getRecipient());
 | 
			
		||||
                data.add(Integer.toString(transaction.getAmount()));
 | 
			
		||||
                data.add(Integer.toString(block.getHeight()));
 | 
			
		||||
                transactionCsvPrinter.printRecord(data);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        transactionCsvPrinter.flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void exportNFTData() throws IOException {
 | 
			
		||||
        Writer NFTWriter = Files.newBufferedWriter(Paths.get("nfts.csv"));
 | 
			
		||||
        CSVPrinter NFTCsvPrinter = new CSVPrinter(NFTWriter, CSVFormat.DEFAULT.withHeader("id", "contract", "owner", "data", "block_height"));
 | 
			
		||||
        int NFTCount = 1;
 | 
			
		||||
        for (Block block : this.blocks) {
 | 
			
		||||
            for (NFT nft : block.getNFTs()) {
 | 
			
		||||
                List<String> data = new ArrayList<>();
 | 
			
		||||
                data.add(Integer.toString(NFTCount++));
 | 
			
		||||
                data.add(nft.getContract());
 | 
			
		||||
                data.add(nft.getOwner());
 | 
			
		||||
                data.add(nft.getData());
 | 
			
		||||
                data.add(Integer.toString(block.getHeight()));
 | 
			
		||||
                NFTCsvPrinter.printRecord(data);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        NFTCsvPrinter.flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getLength() {
 | 
			
		||||
        return length;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +291,16 @@ public class Blockchain {
 | 
			
		|||
        blocks.add(block);
 | 
			
		||||
        length++;
 | 
			
		||||
        if(length != 1 && Main.mainFrame != null) Main.mainFrame.onNewBlockUpdate();
 | 
			
		||||
 | 
			
		||||
        if(importToDatabase) {
 | 
			
		||||
            Main.db.getBlocksTable().insert(block.getHeight(), block.getHash(), block.getPrevHash(), block.getNonce());
 | 
			
		||||
            for (Transaction transaction : block.getTransactions()) {
 | 
			
		||||
                Main.db.getTransactionsTable().insert(transaction.getSender(), transaction.getRecipient(), transaction.getAmount(), block.getHeight());
 | 
			
		||||
            }
 | 
			
		||||
            for (NFT nft : block.getNFTs()) {
 | 
			
		||||
                Main.db.getNFTsTable().insert(nft.getContract(), nft.getOwner(), nft.getData(), block.getHeight());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getAddressBalance(String walletAddress) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,16 +15,12 @@ public class Main {
 | 
			
		|||
    public static void main(String[] args) {
 | 
			
		||||
        db = new DatabaseConnection();
 | 
			
		||||
 | 
			
		||||
        System.out.println(db.getBlocksTable().select());
 | 
			
		||||
        System.out.println(db.getTransactionsTable().select());
 | 
			
		||||
        System.out.println(db.getNFTsTable().select());
 | 
			
		||||
 | 
			
		||||
        Wallet w1 = new Wallet();
 | 
			
		||||
        Wallet w2 = new Wallet();
 | 
			
		||||
        Wallet w3 = new Wallet();
 | 
			
		||||
 | 
			
		||||
        difficulty = 4;
 | 
			
		||||
        blockchain = new Blockchain(w1.getAddress());
 | 
			
		||||
        blockchain = new Blockchain(w1.getAddress(), false);
 | 
			
		||||
 | 
			
		||||
        mainFrame = new MainFrame();
 | 
			
		||||
        mainFrame.setVisible(true);
 | 
			
		||||
| 
						 | 
				
			
			@ -58,17 +54,5 @@ public class Main {
 | 
			
		|||
        } 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()));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -121,6 +121,7 @@ public class MainFrame extends JFrame {
 | 
			
		|||
            else if(status == Block.VerifyCodes.INVALID_POW_SIGNATURE) JOptionPane.showMessageDialog(null, "Invalid proof of work signature found", "Verification", JOptionPane.ERROR_MESSAGE);
 | 
			
		||||
            else if(status == Block.VerifyCodes.INVALID_BLOCK_HASH) JOptionPane.showMessageDialog(null, "Invalid block hash found", "Verification", JOptionPane.ERROR_MESSAGE);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        payForTuitionButton.addActionListener(e -> {
 | 
			
		||||
            int input = JOptionPane.showConfirmDialog(null, "6 DC will be sent to pay tuition\nAre you sure?", "Payment Confirmation", JOptionPane.YES_NO_OPTION);
 | 
			
		||||
            if(input == JOptionPane.YES_OPTION) {
 | 
			
		||||
| 
						 | 
				
			
			@ -146,6 +147,7 @@ public class MainFrame extends JFrame {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        payForAdvanceButton.addActionListener(e -> {
 | 
			
		||||
            int input = JOptionPane.showConfirmDialog(null, "1 DC will be sent to pay advance\nAre you sure?", "Payment Confirmation", JOptionPane.YES_NO_OPTION);
 | 
			
		||||
            if(input == JOptionPane.YES_OPTION) {
 | 
			
		||||
| 
						 | 
				
			
			@ -220,7 +222,27 @@ public class MainFrame extends JFrame {
 | 
			
		|||
        });
 | 
			
		||||
 | 
			
		||||
        exportButton.addActionListener(e -> {
 | 
			
		||||
            int bitfield = 0;
 | 
			
		||||
            if(exportBlockDataCheckBox.isSelected()) bitfield |= Blockchain.ExportType.BLOCK.value;
 | 
			
		||||
            if(exportTransactionDataCheckBox.isSelected()) bitfield |= Blockchain.ExportType.TRANSACTION.value;
 | 
			
		||||
            if(exportNFTDataCheckBox.isSelected()) bitfield |= Blockchain.ExportType.NFT.value;
 | 
			
		||||
 | 
			
		||||
            if(bitfield == 0) {
 | 
			
		||||
                JOptionPane.showMessageDialog(null, "Select something to export");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                bc.exportData(bitfield);
 | 
			
		||||
                int count = Integer.bitCount(bitfield);
 | 
			
		||||
                JOptionPane.showMessageDialog(null, "Successfully exported " + count + " file" + ((count > 1) ? "s" : ""));
 | 
			
		||||
            } catch (IOException ex) {
 | 
			
		||||
                JOptionPane.showMessageDialog(null, "Failed to export data:\n" + ex.getMessage());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            exportBlockDataCheckBox.setSelected(false);
 | 
			
		||||
            exportTransactionDataCheckBox.setSelected(false);
 | 
			
		||||
            exportNFTDataCheckBox.setSelected(false);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        createMintNFTButton.addActionListener(e -> {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue