Add import/export functionality
This commit is contained in:
parent
9a72fb8f77
commit
1d574b3ee1
|
@ -13,5 +13,6 @@
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="mysql.connector.java" level="project" />
|
<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: 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>
|
</component>
|
||||||
</module>
|
</module>
|
5
pom.xml
5
pom.xml
|
@ -20,5 +20,10 @@
|
||||||
<artifactId>mysql-connector-java</artifactId>
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
<version>8.0.30</version>
|
<version>8.0.30</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.davidmoten</groupId>
|
||||||
|
<artifactId>commons-csv</artifactId>
|
||||||
|
<version>1.6.002</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
|
@ -1,13 +1,35 @@
|
||||||
package pl.mikorosa.dziecoin;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class Blockchain {
|
public class Blockchain {
|
||||||
private List<Block> blocks;
|
private List<Block> blocks;
|
||||||
private int length;
|
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<>();
|
blocks = new ArrayList<>();
|
||||||
length = blocks.size();
|
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() {
|
public int getLength() {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
@ -70,6 +291,16 @@ public class Blockchain {
|
||||||
blocks.add(block);
|
blocks.add(block);
|
||||||
length++;
|
length++;
|
||||||
if(length != 1 && Main.mainFrame != null) Main.mainFrame.onNewBlockUpdate();
|
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) {
|
public int getAddressBalance(String walletAddress) {
|
||||||
|
|
|
@ -15,16 +15,12 @@ public class Main {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
db = new DatabaseConnection();
|
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 w1 = new Wallet();
|
||||||
Wallet w2 = new Wallet();
|
Wallet w2 = new Wallet();
|
||||||
Wallet w3 = new Wallet();
|
Wallet w3 = new Wallet();
|
||||||
|
|
||||||
difficulty = 4;
|
difficulty = 4;
|
||||||
blockchain = new Blockchain(w1.getAddress());
|
blockchain = new Blockchain(w1.getAddress(), false);
|
||||||
|
|
||||||
mainFrame = new MainFrame();
|
mainFrame = new MainFrame();
|
||||||
mainFrame.setVisible(true);
|
mainFrame.setVisible(true);
|
||||||
|
@ -58,17 +54,5 @@ public class Main {
|
||||||
} catch (BlockchainIntegrityException e) {
|
} catch (BlockchainIntegrityException e) {
|
||||||
System.out.println("Blockchain Integrity Exception: " + e.getMessage());
|
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_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);
|
else if(status == Block.VerifyCodes.INVALID_BLOCK_HASH) JOptionPane.showMessageDialog(null, "Invalid block hash found", "Verification", JOptionPane.ERROR_MESSAGE);
|
||||||
});
|
});
|
||||||
|
|
||||||
payForTuitionButton.addActionListener(e -> {
|
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);
|
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) {
|
if(input == JOptionPane.YES_OPTION) {
|
||||||
|
@ -146,6 +147,7 @@ public class MainFrame extends JFrame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
payForAdvanceButton.addActionListener(e -> {
|
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);
|
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) {
|
if(input == JOptionPane.YES_OPTION) {
|
||||||
|
@ -220,7 +222,27 @@ public class MainFrame extends JFrame {
|
||||||
});
|
});
|
||||||
|
|
||||||
exportButton.addActionListener(e -> {
|
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 -> {
|
createMintNFTButton.addActionListener(e -> {
|
||||||
|
|
Loading…
Reference in New Issue