Add import/export functionality

This commit is contained in:
Aelita4 2023-01-25 14:01:32 +01:00
parent 9a72fb8f77
commit 1d574b3ee1
5 changed files with 261 additions and 18 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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) {

View File

@ -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()));
}
}

View File

@ -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 -> {