用Java来实现区块链原理并简单模拟开发一个小工具

前几天心血来潮,用Java模拟实现了一个区块链小工具,该工具是单机版的,没有实现联网功能,属于是单节点的工具,但是对于区块链的核心wk逻辑功能都实现了,如:wk、钱包、转账、记账等功能。界面实现用到了Java的swing包。

下面先看下运行的效果:

主界面:

转账:

 下面来看看代码:

首先是界面:

package com.yuanlrc.app;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;

import com.yuanlrc.model.Block;
import com.yuanlrc.model.Transaction;
import com.yuanlrc.thread.MinBlockThread;
import com.yuanlrc.util.CryptoUtil;

public class App extends JFrame {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private JPanel contentPane;
	private JTextField recieverAddressTextField;
	private JTextArea showRstTextArea;
	
	private String address;//当前钱包地址
	
	private List<Block> blockChain;//区块链
	
	private MinBlockThread minBlockThread;
	
	private List<Transaction> txs = new ArrayList<>();
	
	public JProgressBar currentStatusProgressBar;
	private JTextField amountTextField;
	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					App frame = new App();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public App() {
		try {
			UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedLookAndFeelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		address = UUID.randomUUID().toString();
		
		setTitle("【\u733F\u5E01\u6316\u77FF\u5DE5\u5177】钱包地址:" + address +"【当前余额:0】" );
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 717, 571);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		
		JPanel panel = new JPanel();
		panel.setBorder(new TitledBorder(null, "\u524D\u7F6E\u52A8\u4F5C", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		
		JPanel panel_1 = new JPanel();
		panel_1.setBorder(new TitledBorder(null, "\u8F6C\u8D26\u64CD\u4F5C", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		
		JPanel panel_2 = new JPanel();
		panel_2.setBorder(new TitledBorder(null, "\u6316\u77FF\u7ED3\u679C", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		
		JPanel panel_3 = new JPanel();
		panel_3.setBorder(new TitledBorder(null, "\u5F53\u524D\u72B6\u6001", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		GroupLayout gl_contentPane = new GroupLayout(contentPane);
		gl_contentPane.setHorizontalGroup(
			gl_contentPane.createParallelGroup(Alignment.LEADING)
				.addGroup(Alignment.TRAILING, gl_contentPane.createSequentialGroup()
					.addGap(18)
					.addGroup(gl_contentPane.createParallelGroup(Alignment.LEADING, false)
						.addComponent(panel_2, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
						.addComponent(panel_3, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
						.addComponent(panel_1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
						.addComponent(panel, GroupLayout.DEFAULT_SIZE, 651, Short.MAX_VALUE))
					.addGap(22))
		);
		gl_contentPane.setVerticalGroup(
			gl_contentPane.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_contentPane.createSequentialGroup()
					.addContainerGap()
					.addComponent(panel, GroupLayout.PREFERRED_SIZE, 77, GroupLayout.PREFERRED_SIZE)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(panel_1, GroupLayout.PREFERRED_SIZE, 73, GroupLayout.PREFERRED_SIZE)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(panel_3, GroupLayout.DEFAULT_SIZE, 41, Short.MAX_VALUE)
					.addPreferredGap(ComponentPlacement.UNRELATED)
					.addComponent(panel_2, GroupLayout.PREFERRED_SIZE, 300, GroupLayout.PREFERRED_SIZE))
		);
		
		currentStatusProgressBar = new JProgressBar();
		currentStatusProgressBar.setForeground(Color.GREEN);
		currentStatusProgressBar.setBackground(UIManager.getColor("window"));
		GroupLayout gl_panel_3 = new GroupLayout(panel_3);
		gl_panel_3.setHorizontalGroup(
			gl_panel_3.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_panel_3.createSequentialGroup()
					.addContainerGap()
					.addComponent(currentStatusProgressBar, GroupLayout.DEFAULT_SIZE, 619, Short.MAX_VALUE)
					.addContainerGap())
		);
		gl_panel_3.setVerticalGroup(
			gl_panel_3.createParallelGroup(Alignment.LEADING)
				.addComponent(currentStatusProgressBar, GroupLayout.DEFAULT_SIZE, 24, Short.MAX_VALUE)
		);
		panel_3.setLayout(gl_panel_3);
		
		JScrollPane scrollPane = new JScrollPane();
		GroupLayout gl_panel_2 = new GroupLayout(panel_2);
		gl_panel_2.setHorizontalGroup(
			gl_panel_2.createParallelGroup(Alignment.LEADING)
				.addComponent(scrollPane, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 639, Short.MAX_VALUE)
		);
		gl_panel_2.setVerticalGroup(
			gl_panel_2.createParallelGroup(Alignment.LEADING)
				.addComponent(scrollPane, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 276, Short.MAX_VALUE)
		);
		
		showRstTextArea = new JTextArea();
		showRstTextArea.setForeground(Color.GREEN);
		showRstTextArea.setFont(UIManager.getFont("TitledBorder.font"));
		showRstTextArea.setBackground(Color.BLACK);
		scrollPane.setViewportView(showRstTextArea);
		panel_2.setLayout(gl_panel_2);
		
		JLabel label = new JLabel("\u8F6C\u8D26\u5730\u5740");
		
		recieverAddressTextField = new JTextField();
		recieverAddressTextField.setColumns(10);
		
		JButton submitTransButton = new JButton("\u786E\u5B9A\u8F6C\u8D26");
		submitTransButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				//转账
				String recieverAddress = recieverAddressTextField.getText();
				if(recieverAddress == null || "".equals(recieverAddress)){
					JOptionPane.showMessageDialog(App.this, "请输入转账地址!");
					return;
				}
				String amount = amountTextField.getText();
				if(amount == null || "".equals(amount)){
					JOptionPane.showMessageDialog(App.this, "请输入转账金额!");
					return;
				}
				try {
					double a = Double.valueOf(amount);
					double walletBalance = getWalletBalance(blockChain, address);
					if(walletBalance < a){
						JOptionPane.showMessageDialog(App.this, "您的余额不足!");
						return;
					}
					int showConfirmDialog = JOptionPane.showConfirmDialog(App.this, "确定转账?");
					if(showConfirmDialog == JOptionPane.OK_OPTION){
						Transaction tx = new Transaction(CryptoUtil.UUID(), address, recieverAddress, a);
						txs.add(tx);
						String title = getTitle();
						title = title.substring(0,title.lastIndexOf("【"));
						walletBalance = getWalletBalance(blockChain, address);
						title += "【当前余额:" + walletBalance + "】";
						setTitle(title);
						JOptionPane.showMessageDialog(App.this, "转账成功!");
						showRstTextArea.setText(showRstTextArea.getText() + "\n成功转账:" +a+ "个猿币,对方账户【"+recieverAddress+"】,当前猿币余额:" + walletBalance);
					}
				} catch (Exception e) {
					JOptionPane.showMessageDialog(App.this, "请输入正确的数字类型!");
					return;
				}
				
			}
		});
		
		JLabel label_1 = new JLabel("\u8F6C\u8D26\u91D1\u989D");
		
		amountTextField = new JTextField();
		amountTextField.setColumns(10);
		GroupLayout gl_panel_1 = new GroupLayout(panel_1);
		gl_panel_1.setHorizontalGroup(
			gl_panel_1.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_panel_1.createSequentialGroup()
					.addContainerGap()
					.addComponent(label)
					.addGap(18)
					.addComponent(recieverAddressTextField, GroupLayout.DEFAULT_SIZE, 192, Short.MAX_VALUE)
					.addGap(18)
					.addComponent(label_1)
					.addGap(27)
					.addComponent(amountTextField, GroupLayout.PREFERRED_SIZE, 122, GroupLayout.PREFERRED_SIZE)
					.addGap(46)
					.addComponent(submitTransButton)
					.addGap(29))
		);
		gl_panel_1.setVerticalGroup(
			gl_panel_1.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_panel_1.createSequentialGroup()
					.addContainerGap()
					.addGroup(gl_panel_1.createParallelGroup(Alignment.BASELINE)
						.addComponent(recieverAddressTextField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addComponent(label)
						.addComponent(submitTransButton)
						.addComponent(amountTextField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addComponent(label_1))
					.addContainerGap(16, Short.MAX_VALUE))
		);
		panel_1.setLayout(gl_panel_1);
		
		JButton createInitBlockChainButton = new JButton("\u521B\u5EFA\u521D\u59CB\u533A\u5757\u94FE");
		createInitBlockChainButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				//初始化区块链
				if(blockChain != null){
					JOptionPane.showMessageDialog(App.this, "请勿重复初始化!");
					return;
				}
				blockChain = new ArrayList<Block>();
				showRstTextArea.setText("初始化完成!");
			}
		});
		createInitBlockChainButton.setFont(new Font("黑体", Font.PLAIN, 14));
		
		JButton createFirstBlockButton = new JButton("\u751F\u6210\u521B\u4E16\u533A\u5757");
		createFirstBlockButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				//生成创世区块
				if(blockChain == null){
					JOptionPane.showMessageDialog(App.this, "请先初始化创建区块链!");
					return;
				}
				if(blockChain.size() > 0){
					JOptionPane.showMessageDialog(App.this, "创世区块已生成,请勿重复创建!");
					return;
				}
				String hash = UUID.randomUUID().toString();
				Block block = new Block(1, System.currentTimeMillis(), txs, 0, hash, hash);
				showRstTextArea.setText(showRstTextArea.getText() + "\n创世区块已建立!");
				blockChain.add(block);
				showRstTextArea.setText(showRstTextArea.getText() + "\n创世区块已加入到区块链!");
			}
		});
		createFirstBlockButton.setFont(new Font("黑体", Font.PLAIN, 14));
		
		JButton startMineCoinButton = new JButton("\u5F00\u59CB\u6316\u733F\u5E01");
		startMineCoinButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				//开始挖矿
				if("停止挖矿".equals(startMineCoinButton.getText())){
					stopMinBlock();
					startMineCoinButton.setText("开始挖矿");
					return;
				}
				if(blockChain == null){
					JOptionPane.showMessageDialog(App.this, "请先初始化创建区块链!");
					return;
				}
				if(blockChain.size() == 0){
					JOptionPane.showMessageDialog(App.this, "请先生成创世区块!");
					return;
				}
				
				startMineBlock();
				startMineCoinButton.setText("停止挖矿");
			}
		});
		startMineCoinButton.setFont(new Font("黑体", Font.PLAIN, 14));
		GroupLayout gl_panel = new GroupLayout(panel);
		gl_panel.setHorizontalGroup(
			gl_panel.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_panel.createSequentialGroup()
					.addGap(45)
					.addComponent(createInitBlockChainButton)
					.addPreferredGap(ComponentPlacement.RELATED, 91, Short.MAX_VALUE)
					.addComponent(createFirstBlockButton)
					.addGap(83)
					.addComponent(startMineCoinButton)
					.addGap(69))
		);
		gl_panel.setVerticalGroup(
			gl_panel.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_panel.createSequentialGroup()
					.addGroup(gl_panel.createParallelGroup(Alignment.BASELINE)
						.addComponent(startMineCoinButton, GroupLayout.PREFERRED_SIZE, 40, GroupLayout.PREFERRED_SIZE)
						.addComponent(createInitBlockChainButton, GroupLayout.PREFERRED_SIZE, 39, GroupLayout.PREFERRED_SIZE)
						.addComponent(createFirstBlockButton, GroupLayout.PREFERRED_SIZE, 41, GroupLayout.PREFERRED_SIZE))
					.addContainerGap(3, Short.MAX_VALUE))
		);
		panel.setLayout(gl_panel);
		contentPane.setLayout(gl_contentPane);
	}
	
	protected void stopMinBlock() {
		// TODO Auto-generated method stub
		minBlockThread.interrupt();
		currentStatusProgressBar.setValue(100);
	}

	/**
	 * 挖矿
	 * @param blockchain 整个区块链
	 * @param txs 需记账交易记录,包含
	 * @return
	 */
	private void startMineBlock() {
		minBlockThread = new MinBlockThread(showRstTextArea, blockChain, txs, this, address);
		minBlockThread.start();
		currentStatusProgressBar.setValue(0);
    }
	
	/**
	 * 查询余额
	 * @param blockchain
	 * @param address
	 * @return
	 */
	public double getWalletBalance(List<Block> blockchain, String address) {
		double balance = 0;
		for (Block block : blockchain) {
	        List<Transaction> transactions = block.getTransactions();
	        for (Transaction transaction : transactions) {
	            if (address.equals(transaction.getRecipient())) {
	            	balance += transaction.getAmount();
                }
	            if (address.equals(transaction.getSender())) {
	            	balance -= transaction.getAmount();
                }
            }
	        break;
        }
		return balance;
	}
}

 实体类:

package com.yuanlrc.model;

import java.util.List;

/**
 * 区块
 * @author llq
 *
 */
public class Block {

	/**
	 * 区块索引号
	 */
	private int index;
	/**
	 * 当前区块的hash值,区块唯一标识
	 */
	private String hash;
	/**
	 * 生成区块的时间戳
	 */
	private long timestamp;
	/**
	 * 当前区块的交易集合
	 */
	private List<Transaction> transactions;
	/**
	 * 工作量证明,计算正确hash值的次数
	 */
	private int nonce;
	/**
	 * 前一个区块的hash值
	 */
	private String previousHash;
	
	public Block(int index, long timestamp, List<Transaction> transactions, int nonce, String previousHash, String hash) {
		super();
		this.index = index;
		this.timestamp = timestamp;
		this.transactions = transactions;
		this.nonce = nonce;
		this.previousHash = previousHash;
		this.hash = hash;
	}
	
	public int getIndex() {
		return index;
	}
	public void setIndex(int index) {
		this.index = index;
	}
	public String getHash() {
		return hash;
	}
	public void setHash(String hash) {
		this.hash = hash;
	}
	public long getTimestamp() {
		return timestamp;
	}
	public void setTimestamp(long timestamp) {
		this.timestamp = timestamp;
	}
	public List<Transaction> getTransactions() {
		return transactions;
	}
	public void setTransactions(List<Transaction> transactions) {
		this.transactions = transactions;
	}
	public int getNonce() {
		return nonce;
	}
	public void setNonce(int nonce) {
		this.nonce = nonce;
	}
	public String getPreviousHash() {
		return previousHash;
	}
	public void setPreviousHash(String previousHash) {
		this.previousHash = previousHash;
	}
	
	
}
package com.yuanlrc.model;
/**
 * 交易集合
 * @author llq
 *
 */
public class Transaction {
	/**
	 * 交易唯一标识
	 */
	private String id;
	/**
	 * 交易发送方
	 */
	private String sender;
	/**
	 * 交易接收方
	 */
	private String recipient;
	/**
	 * 交易金额
	 */
	private double amount;
	
	
	public Transaction(String id, String sender, String recipient, double amount) {
		super();
		this.id = id;
		this.sender = sender;
		this.recipient = recipient;
		this.amount = amount;
	}
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getSender() {
		return sender;
	}
	public void setSender(String sender) {
		this.sender = sender;
	}
	public String getRecipient() {
		return recipient;
	}
	public void setRecipient(String recipient) {
		this.recipient = recipient;
	}
	public double getAmount() {
		return amount;
	}
	public void setAmount(double amount) {
		this.amount = amount;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Transaction other = (Transaction) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}
}

 加密类:

package com.yuanlrc.util;

import java.security.MessageDigest;
import java.util.UUID;


/**
 * 加密类
 * 
 * @author aaron.rao
 *
 */
public class CryptoUtil {
	private CryptoUtil() {
	}

	public static String SHA256(String str) {
		MessageDigest messageDigest;
		String encodeStr = "";
		try {
			messageDigest = MessageDigest.getInstance("SHA-256");
			messageDigest.update(str.getBytes("UTF-8"));
			encodeStr = byte2Hex(messageDigest.digest());
		} catch (Exception e) {
			System.out.println("getSHA256 is error" + e.getMessage());
		}
		return encodeStr;
	}


	public static String UUID() {
		return UUID.randomUUID().toString().replaceAll("\\-", "");
	}

	private static String byte2Hex(byte[] bytes) {
		StringBuilder builder = new StringBuilder();
		String temp;
		for (int i = 0; i < bytes.length; i++) {
			temp = Integer.toHexString(bytes[i] & 0xFF);
			if (temp.length() == 1) {
				builder.append("0");
			}
			builder.append(temp);
		}
		return builder.toString();
	}

}

 解谜类:

package com.yuanlrc.util;

import java.security.Key;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;

public class DESUtil {
	
	/**
     * 偏移变量,固定占8位字节
     */
    private final static String IV_PARAMETER = "12345678";
    /**
     * 密钥算法
     */
    private static final String ALGORITHM = "DES";
    /**
     * 加密/解密算法-工作模式-填充模式
     */
    private static final String CIPHER_ALGORITHM = "DES/CBC/PKCS5Padding";
    /**
     * 默认编码
     */
    private static final String CHARSET = "UTF-8";
	
	/**
     * 计算加密key
     * @param password
     * @return
     * @throws Exception
     */
    private static Key generateKey(String password) throws Exception {
        DESKeySpec dks = new DESKeySpec(password.getBytes(CHARSET));
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
        return keyFactory.generateSecret(dks);
    }

    /**
     * 计算加密后的值
     * @param key
     * @param data
     * @return
     */
    public static String encrypt(String key, String data) {
        if (key== null || key.length() < 8) {
            throw new RuntimeException("加密失败,key不能小于8位");
        }
        if (data == null)
            return null;
        try {
            Key secretKey = generateKey(key);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes(CHARSET));
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
            byte[] bytes = cipher.doFinal(data.getBytes(CHARSET));
 
            //JDK1.8及以上可直接使用Base64,JDK1.7及以下可以使用BASE64Encoder
            //Android平台可以使用android.util.Base64
            return new String(Base64.getEncoder().encode(bytes));
 
        } catch (Exception e) {
            e.printStackTrace();
            return data;
        }
    }

    /**
     * DES解密字符串
     *
     * @param key 解密密码,长度不能够小于8位
     * @param data 待解密字符串
     * @return 解密后内容
     */
    public static String decrypt(String key, String data) {
        if (key== null || key.length() < 8) {
            throw new RuntimeException("解密失败,key不能小于8位");
        }
        if (data == null)
            return null;
        try {
            Key secretKey = generateKey(key);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes(CHARSET));
            cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
            return new String(cipher.doFinal(Base64.getDecoder().decode(data.getBytes(CHARSET))), CHARSET);
        } catch (Exception e) {
            e.printStackTrace();
            return data;
        }
    }

    
    public static void main(String[] args){
    	System.out.println(encrypt("muyi_ylrc", System.currentTimeMillis()+""));
    	System.out.println(decrypt("muyi_ylrc",encrypt("muyi_ylrc", System.currentTimeMillis()+"")));
    	System.out.println(encrypt("muyi_ylrc", "159083378454884859#17347843162"));
    	System.out.println(decrypt("muyi_ylrc","snSZdXC3NRZgoPaigiNyvm0A+Piwa09LDwqhvFZwalU="));
    }
}

猜你喜欢

转载自blog.csdn.net/yuancodenet/article/details/128017483