基于Java 实现把区块链变成数字货币
1. 交易数据
/**
* 转账数据信息
* 转账的要素:
* 付款人
* 收款人
* 转账金额
* 转账时间
*/
public class Transaction {
private String from;
private String to;
private Long amount;
// private Date timeStamp; 转账时间 最终以上链时间为准,顾需要再区块加一个时间
public Transaction() {
}
public Transaction(String from, String to, Long amount) {
this.from = from;
this.to = to;
this.amount = amount;
// this.timeStamp = timeStamp;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public Long getAmount() {
return amount;
}
public void setAmount(Long amount) {
this.amount = amount;
}
// public Date getTimeStamp() {
// return timeStamp;
// }
//
// public void setTimeStamp(Date timeStamp) {
// this.timeStamp = timeStamp;
// }
@Override
public String toString() {
return "Transaction{" +
"from='" + from + '\'' +
", to='" + to + '\'' +
", amount=" + amount +
'}';
}
}
2. 创建区块
import java.time.LocalDateTime;
import java.util.List;
/**
* 区块链 里边存放的数据是:
* data 数据
* preHash 上一个区块的hash值
* hash 自己的 hash值 它是由存储在区块链的信息 计算出来的(data + 之前区块的哈希值) ,采用 sha256 算法
*/
public class Block {
//当区块链上边存储的数据是转账数据的时候 我们就可以把它变成数字货币
// private Object data;
// 现在要把Object 的 data 变为 List Transaction 对象
private List<Transaction> transactions;
private String preHash;
private String hash;
// 初始随机值
private Long nonce;
// 区块的上链时间
private LocalDateTime timestamp;
// public Block(Object data, String preHash) {
// this.data = data;
// this.preHash = preHash;
// this.hash = this.computeHash();
// this.nonce = 1L;
// }
public Block () {
}
public Block(List<Transaction> transactions, String preHash) {
this.transactions = transactions;
this.preHash = preHash;
this.hash = this.computeHash();
this.nonce = 1L;
this.timestamp = LocalDateTime.now();
}
// public Object getData() {
// return data;
// }
//
// public void setData(Object data) {
// this.data = data;
// }
public List<Transaction> getTransactions() {
return transactions;
}
public void setTransactions(List<Transaction> transactions) {
this.transactions = transactions;
}
public String getPreHash() {
return preHash;
}
public void setPreHash(String preHash) {
this.preHash = preHash;
}
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
public Long getNonce() {
return nonce;
}
public void setNonce(Long nonce) {
this.nonce = nonce;
}
public LocalDateTime getTimestamp() {
return timestamp;
}
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
public String computeHash() {
// 要加入随机值 nonce
// return new Encrypt().SHA256(this.data + this.preHash + this.nonce);
// timestamp 参与改动hash 的计算
return new Encrypt().SHA256(this.transactions.toString() + this.preHash + this.nonce + this.timestamp);
}
// 开头前n 位为0的 hash值
public String getAnswer(Integer difficulty) {
String answer = "";
for (int i = 0; i < difficulty; i++) {
answer +="0";
}
return answer;
}
// 计算符合区块链难度要求的hash 这个过程 俗称 挖矿
public void mine(Integer difficulty) {
while (true) {
this.hash = this.computeHash();
if (! this.hash.substring(0,difficulty).equals(this.getAnswer(difficulty))) {
// nonce ++ 才会得到不同的hash值
this.nonce ++ ;
this.hash =this.computeHash();
} else {
break;
}
}
System.out.println("挖矿结束:"+ this.hash);
}
// @Override
// public String toString() {
// return "Block{" +
// "data=" + data +
// ", preHash='" + preHash + '\'' +
// ", hash='" + hash + '\'' +
// ", nonce=" + nonce +
// '}';
// }
@Override
public String toString() {
return "Block{" +
"transactions=" + transactions +
", preHash='" + preHash + '\'' +
", hash='" + hash + '\'' +
", nonce=" + nonce +
", timestamp=" + timestamp +
'}';
}
}
3. 创建链 并测试数据
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 参考:https://github.com/ycraaron/LuotuoCoin
* 区块的链
*/
public class Chain {
private List<Block> chain = new ArrayList<>();
// 交易池
private List<Transaction> transactionPool;
// 每挖出来一个区块会有50个比特币的产生会发放到矿工这里 这是也是从50开始为初始值
private Long minerReward;
// 工作量难度
private Integer difficulty;
public Chain() {
chain.add(bigBang());
this.transactionPool = new ArrayList<>();
this.minerReward = 50L;
this.difficulty = 5;
}
public Chain(List<Block> chain) {
chain.add(bigBang());
this.chain.addAll(chain);
}
/**
* 每个区块都会生成一个祖先区块
* @return
*/
public Block bigBang() {
// 创建祖先节点 hash为空
Transaction transaction = new Transaction("我是祖先","",0L);
return new Block(Arrays.asList(transaction),"");
// Block block = new Block();
// return new Block("祖先","");
// return block;
}
/**
* 获取最后一个区块的信息
* @return
*/
public Block getLatestBlock() {
return getChain().get(this.chain.size()-1);
}
/**
* 添加区块到区块链上
* 找到最近一个block的hash
* 这个hash就是新区块的preHash
*/
public void addBlockToChain(Block newBlock) {
newBlock.setPreHash(this.getLatestBlock().computeHash());
newBlock.setHash(newBlock.computeHash());
// 比特币 控制 每十分钟出一个区块了 就是通过 ProofOfWork 工作机制
// 这个hash 需要满足一个区块链设置的条件
newBlock.mine(this.difficulty);
this.chain.add(newBlock);
}
/**
* 添加 transaction 到 transactionPool 里边
* @param transaction 交易对象
*/
public void addTransaction(Transaction transaction) {
this.transactionPool.add(transaction);
}
/**
* 现在所有的区块都不是从外部传进来的,每当 被发出来的时候,矿工会从池子中 去拿 transaction, 以transaction数据为基础去
* 新建这个block,意味着 block 新建发生在链上, 发生在挖transaction这步操作里边。
* @param minerRewardAddress 挖矿人的address, 矿工调用的时候传过来的
* @return
*/
public void mineTransactionPool (String minerRewardAddress) {
// 1. 发放矿工奖励 矿工的奖励是以transaction的形式发放
// 发放地址为空 因为这是链来发放的
// minerReward 由链产生并发放
Transaction minerRewardTransaction = new Transaction("", minerRewardAddress, this.minerReward);
this.transactionPool.add(minerRewardTransaction);
// 2. 挖矿 每个区块从链上新建 而不是外部传过来
// 现实生活当中 每个 Block 可以容纳 一定量的 Transaction,在挖矿的时候 选择收益最高的矿去挖,所以它会从transactionPool
// 调取最多收益的去挖这个 block ,这种情况就不考虑了,简单起见 每次就报所有的block 去挖
Block newBlock = new Block(this.transactionPool, this.getLatestBlock().getHash());
newBlock.mine(this.difficulty);
// 3. 添加区块到区块链
this.chain.add(newBlock);
// 清空 这次挖出来的 transaction Pool
// this.transactionPool.clear();
}
public List<Block> getChain() {
return chain;
}
public void setChain(List<Block> chain) {
this.chain = chain;
}
/**
* 验证这个当前的区块链是否合法
* 当前的区块是否被篡改
* 我们要验证区块的preHash是否等于previous区块hash
*/
public Boolean validateBlockChain() {
// 创始区块的校验
if (this.chain.size() == 1) {
// 比较创始区块的hash值
if (! this.chain.get(0).getHash().equalsIgnoreCase(this.chain.get(0).computeHash())) {
return false;
}
return true;
} else {
// this.chain[1] 是第二个区块
// 我们从第二个区块开始验证
// 验证到最后一个区块 this.chain.size() -1
for (int i = 1; i <= this.chain.size() -1; i++) {
Block blockValidate = this.chain.get(i);
// 验证当前数据是否被篡改
if (!blockValidate.getHash().equalsIgnoreCase(blockValidate.computeHash())) {
System.out.println("数据被篡改!");
return false;
}
// 验证区块的previousHash 是否等于 previous区块的hash
Block previousBlock = this.chain.get(i-1);
//
if (!blockValidate.getPreHash().equalsIgnoreCase(previousBlock.getHash())) {
System.out.println("前后区块链接断裂!");
return false;
}
}
return true;
}
}
@Override
public String toString() {
return "Chain{" +
"chain=" + chain +
", transactionPool=" + transactionPool +
", minerReward=" + minerReward +
", difficulty=" + difficulty +
'}';
}
public static void main(String[] args) {
// Chain chain = new Chain();
//// System.out.println(chain.validateBlockChain());
// Block block1 = new Block("转账10元","1234");
// chain.addBlockToChain(block1);
// Block block2 = new Block("转账20元","1234");
// chain.addBlockToChain(block2);
// System.out.println(chain);
//
//// System.out.println(chain.validateBlockChain());
//// // 篡改区块链上的数据
// chain.getChain().get(1).setData("转账30元");
//// System.out.println(chain.validateBlockChain());
// // 篡改区块链的hash值
//// chain.getChain().get(1).setHash(chain.getChain().get(1).computeHash());
// chain.getChain().get(1).mine(5);
// System.out.println(chain);
// System.out.println(chain.validateBlockChain());
Chain chainCoin = new Chain();
Transaction t1 = new Transaction("add1", "add2", 10L);
Transaction t2 = new Transaction("add2", "add1", 5L);
chainCoin.addTransaction(t1);
chainCoin.addTransaction(t2);
// System.out.println(chainCoin);
chainCoin.mineTransactionPool("add3");
// System.out.println(chainCoin);
System.out.println(chainCoin.getChain().get(1));
System.out.println(chainCoin.getChain().get(1).getTransactions());
}
}
4. SHA-256 算法
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
*JAVA 已经实现了 SHA-256 和 SHA-512 两种 Hash 算法
*利用 java.security.MessageDigest 调用已经集成的 Hash 算法
*创建 Encrypt 对象,并调用 SHA256 或者 SHA512 并传入要加密的文本信息,分别得到 SHA-256 或 SHA-512 两种被加密的 hash 串。
*若要改为 MD5 算法,修改传入参数 strType 为 "MD5" 即可得到 MD5 加密功能。
*/
public class Encrypt {
/**
* 传入文本内容,返回 SHA-256 串
*
* @param strText
* @return
*/
public String SHA256(final String strText) {
return SHA(strText, "SHA-256");
}
/**
* 传入文本内容,返回 SHA-512 串
*
* @param strText
* @return
*/
public String SHA512(final String strText) {
return SHA(strText, "SHA-512");
}
/**
* 字符串 SHA 加密
*
* @param strText
* @param strType
* @return
*/
private String SHA(final String strText, final String strType) {
// 返回值
String strResult = null;
// 是否是有效字符串
if (strText != null && strText.length() > 0) {
try {
// SHA 加密开始
// 创建加密对象 并傳入加密類型
MessageDigest messageDigest = MessageDigest.getInstance(strType);
// 传入要加密的字符串
messageDigest.update(strText.getBytes());
// 得到 byte 類型结果
byte byteBuffer[] = messageDigest.digest();
// 將 byte 轉換爲 string
StringBuffer strHexString = new StringBuffer();
// 遍歷 byte buffer
for (int i = 0; i < byteBuffer.length; i++) {
String hex = Integer.toHexString(0xff & byteBuffer[i]);
if (hex.length() == 1) {
strHexString.append('0');
}
strHexString.append(hex);
}
// 得到返回結果
strResult = strHexString.toString();
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
return strResult;
}
}