首页 > 技术文章 > JAVA版本区块链钱包核心代码

vxianfeng 2018-10-04 14:14 原文

Block.java

package com.ppblock.blockchain.core;

import java.io.Serializable;

/**
 * 区块
 * @author yangjian
 * @since 18-4-6
 */
public class Block implements Serializable {

	/**
	 * 区块 Header
	 */
	private BlockHeader header;
	/**
	 * 区块 Body
	 */
	private BlockBody body;

	public Block(BlockHeader header, BlockBody body) {
		this.header = header;
		this.body = body;
	}

	public Block() {
	}

	public BlockHeader getHeader() {
		return header;
	}

	public void setHeader(BlockHeader header) {
		this.header = header;
	}

	public BlockBody getBody() {
		return body;
	}

	public void setBody(BlockBody body) {
		this.body = body;
	}

	@Override
	public String toString() {
		return "Block{" +
				"header=" + header +
				", body=" + body.toString() +
				'}';
	}
}

  BlockBody.java

package com.ppblock.blockchain.core;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * 区块数据
 * @author yangjian
 * @since 18-4-8
 */
public class BlockBody implements Serializable {

	/**
	 * 区块所包含的交易记录
	 */
	private List<Transaction> transactions;

	public BlockBody(List<Transaction> transactions) {
		this.transactions = transactions;
	}

	public BlockBody() {
		this.transactions = new ArrayList<>();
	}

	public List<Transaction> getTransactions() {
		return transactions;
	}

	public void setTransactions(List<Transaction> transactions) {
		this.transactions = transactions;
	}

	/**
	 * 追加一笔交易
	 * @param transaction
	 */
	public void addTransaction(Transaction transaction) {
		transactions.add(transaction);
	}

	@Override
	public String toString() {
		return "BlockBody{" +
				"transactions=" + transactions +
				'}';
	}
}

  BlockChain.java

package com.ppblock.blockchain.core;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.ppblock.blockchain.crypto.Credentials;
import com.ppblock.blockchain.crypto.Keys;
import com.ppblock.blockchain.crypto.Sign;
import com.ppblock.blockchain.db.DBAccess;
import com.ppblock.blockchain.enums.TransactionStatusEnum;
import com.ppblock.blockchain.event.MineBlockEvent;
import com.ppblock.blockchain.event.SendTransactionEvent;
import com.ppblock.blockchain.mine.Miner;
import com.ppblock.blockchain.net.ApplicationContextProvider;
import com.ppblock.blockchain.net.base.Node;
import com.ppblock.blockchain.net.client.AppClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

/**
 * 区块链主类
 * @author yangjian
 * @since 18-4-6
 */
@Component
public class BlockChain {

	private static Logger logger = LoggerFactory.getLogger(BlockChain.class);

	@Autowired
	private DBAccess dbAccess;

	@Autowired
	private AppClient appClient;

	@Autowired
	private Miner miner;

	@Autowired
	private TransactionPool transactionPool;

	@Autowired
	private TransactionExecutor executor;
	/**
	 * 挖取一个区块
	 * @return
	 */
	public Block mining() throws Exception {

		Optional<Block> lastBlock = getLastBlock();
		Block block = miner.newBlock(lastBlock);
		transactionPool.getTransactions().forEach(e -> block.getBody().addTransaction(e));
		executor.run(block);
		//清空交易池
		transactionPool.clearTransactions();
		//存储区块
		dbAccess.putLastBlockIndex(block.getHeader().getIndex());
		dbAccess.putBlock(block);
		logger.info("Find a New Block, {}", block);

		//触发挖矿事件,并等待其他节点确认区块
		ApplicationContextProvider.publishEvent(new MineBlockEvent(block));
		return block;
	}

	/**
	 * 发送交易
	 * @param credentials 交易发起者的凭证
	 * @param to 交易接收者
	 * @param amount
	 * @param data 交易附言
	 * @return
	 * @throws Exception
	 */
	public Transaction sendTransaction(Credentials credentials, String to, BigDecimal amount, String data) throws
			Exception {

		//校验付款和收款地址
		Preconditions.checkArgument(to.startsWith("0x"), "收款地址格式不正确");
		Preconditions.checkArgument(!credentials.getAddress().equals(to), "收款地址不能和发送地址相同");

		//构建交易对象
		Transaction transaction = new Transaction(credentials.getAddress(), to, amount);
		transaction.setPublicKey(Keys.publicKeyEncode(credentials.getEcKeyPair().getPublicKey().getEncoded()));
		transaction.setStatus(TransactionStatusEnum.APPENDING);
		transaction.setData(data);
		transaction.setTxHash(transaction.hash());
		//签名
		String sign = Sign.sign(credentials.getEcKeyPair().getPrivateKey(), transaction.toString());
		transaction.setSign(sign);

		//先验证私钥是否正确
		if (!Sign.verify(credentials.getEcKeyPair().getPublicKey(), sign, transaction.toString())) {
			throw new RuntimeException("私钥签名验证失败,非法的私钥");
		}

		//打包数据到交易池
		transactionPool.addTransaction(transaction);

		//触发交易事件,向全网广播交易,并等待确认
		ApplicationContextProvider.publishEvent(new SendTransactionEvent(transaction));
		return transaction;
	}

	/**
	 * 获取最后一个区块
	 * @return
	 */
	public Optional<Block> getLastBlock() {
		return dbAccess.getLastBlock();
	}

	/**
	 * 添加一个节点
	 * @param ip
	 * @param port
	 * @return
	 */
	public void addNode(String ip, int port) throws Exception {

		appClient.addNode(ip, port);
		Node node = new Node(ip, port);
		dbAccess.addNode(node);
	}
}

  BlockHeader.java

package com.ppblock.blockchain.core;

import com.ppblock.blockchain.crypto.Hash;

import java.io.Serializable;
import java.math.BigInteger;

/**
 * 区块头
 * @author yangjian
 * @since 18-4-6
 */
public class BlockHeader implements Serializable {

	/**
	 * 区块高度
	 */
	private Integer index;
	/**
	 * 难度指标
	 */
	private BigInteger difficulty;
	/**
	 * PoW 问题的答案
	 */
	private Long nonce;
	/**
	 * 时间戳
	 */
	private Long timestamp;
	/**
	 * 区块 Hash
	 */
	private String hash;
	/**
	 * 上一个区块的 hash 地址
	 */
	private String previousHash;

	public BlockHeader(Integer index, String previousHash) {
		this.index = index;
		this.timestamp = System.currentTimeMillis();
		this.previousHash = previousHash;
	}

	public BlockHeader() {
		this.timestamp = System.currentTimeMillis();
	}

	public Integer getIndex() {
		return index;
	}

	public void setIndex(Integer index) {
		this.index = index;
	}

	public BigInteger getDifficulty() {
		return difficulty;
	}

	public void setDifficulty(BigInteger difficulty) {
		this.difficulty = difficulty;
	}

	public Long getNonce() {
		return nonce;
	}

	public void setNonce(Long nonce) {
		this.nonce = nonce;
	}

	public Long getTimestamp() {
		return timestamp;
	}

	public void setTimestamp(Long timestamp) {
		this.timestamp = timestamp;
	}

	public String getPreviousHash() {
		return previousHash;
	}

	public void setPreviousHash(String previousHash) {
		this.previousHash = previousHash;
	}

	public String getHash() {
		return hash;
	}

	public void setHash(String hash) {
		this.hash = hash;
	}

	@Override
	public String toString() {
		return "BlockHeader{" +
				"index=" + index +
				", difficulty=" + difficulty +
				", nonce=" + nonce +
				", timestamp=" + timestamp +
				", hash='" + hash + '\'' +
				", previousHash='" + previousHash + '\'' +
				'}';
	}

	/**
	 * 获取区块头的 hash 值
	 * @return
	 */
	public String hash() {
		return Hash.sha3("BlockHeader{" +
				"index=" + index +
				", difficulty=" + difficulty +
				", nonce=" + nonce +
				", timestamp=" + timestamp +
				", previousHash='" + previousHash + '\'' +
				'}');
	}
}

  Transaction.java

package com.ppblock.blockchain.core;

import com.ppblock.blockchain.crypto.Hash;
import com.ppblock.blockchain.enums.TransactionStatusEnum;

import java.math.BigDecimal;

/**
 * 交易对象
 * @author yangjian
 * @since 18-4-6
 */
public class Transaction {

	/**
	 * 付款人地址
	 */
	private String from;
	/**
	 * 付款人签名
	 */
	private String sign;
	/**
	 * 收款人地址
	 */
	private String to;
	/**
	 * 收款人公钥
	 */
	private String publicKey;
	/**
	 * 交易金额
	 */
	private BigDecimal amount;
	/**
	 * 交易时间戳
	 */
	private Long timestamp;
	/**
	 * 交易 Hash 值
	 */
	private String txHash;
	/**
	 * 交易状态
	 */
	private TransactionStatusEnum status = TransactionStatusEnum.SUCCESS;
	/**
	 * 交易错误信息
	 */
	private String errorMessage;
	/**
	 * 附加数据
	 */
	private String data;

	public Transaction(String from, String to, BigDecimal amount) {
		this.from = from;
		this.to = to;
		this.amount = amount;
		this.timestamp = System.currentTimeMillis();
	}

	public Transaction() {
		this.timestamp = System.currentTimeMillis();
	}

	public String getFrom() {
		return from;
	}

	public void setFrom(String from) {
		this.from = from;
	}

	public String getSign() {
		return sign;
	}

	public void setSign(String sign) {
		this.sign = sign;
	}

	public String getTo() {
		return to;
	}

	public void setTo(String to) {
		this.to = to;
	}

	public String getPublicKey() {
		return publicKey;
	}

	public void setPublicKey(String publicKey) {
		this.publicKey = publicKey;
	}

	public BigDecimal getAmount() {
		return amount;
	}

	public void setAmount(BigDecimal amount) {
		this.amount = amount;
	}

	public Long getTimestamp() {
		return timestamp;
	}

	public void setTimestamp(Long timestamp) {
		this.timestamp = timestamp;
	}

	public String getTxHash() {
		return txHash;
	}

	public void setTxHash(String txHash) {
		this.txHash = txHash;
	}

	public TransactionStatusEnum getStatus() {
		return status;
	}

	public void setStatus(TransactionStatusEnum status) {
		this.status = status;
	}

	public String getErrorMessage() {
		return errorMessage;
	}

	public void setErrorMessage(String errorMessage) {
		this.errorMessage = errorMessage;
	}

	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}

	/**
	 * 计算交易信息的Hash值
	 * @return
	 */
	public String hash() {
		return Hash.sha3(this.toString());
	}

	@Override
	public String toString() {
		return "Transaction{" +
				"from='" + from + '\'' +
				", to='" + to + '\'' +
				", publicKey=" + publicKey +
				", amount=" + amount +
				", timestamp=" + timestamp +
				", data='" + data + '\'' +
				'}';
	}
}

  TransactionExecutor.java

package com.ppblock.blockchain.core;

import com.google.common.base.Optional;
import com.ppblock.blockchain.account.Account;
import com.ppblock.blockchain.crypto.Keys;
import com.ppblock.blockchain.crypto.Sign;
import com.ppblock.blockchain.db.DBAccess;
import com.ppblock.blockchain.enums.TransactionStatusEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

/**
 * 交易执行器
 * @author yangjian
 * @since 18-4-23
 */
@Component
public class TransactionExecutor {

	@Autowired
	private DBAccess dbAccess;

	@Autowired
	private TransactionPool transactionPool;

	/**
	 * 执行区块中的交易
	 * @param block
	 */
	public void run(Block block) throws Exception {

		for (Transaction transaction : block.getBody().getTransactions()) {
			synchronized (this) {

				Optional<Account> recipient = dbAccess.getAccount(transaction.getTo());
				//如果收款地址账户不存在,则创建一个新账户
				if (!recipient.isPresent()) {
					recipient = Optional.of(new Account(transaction.getTo(), BigDecimal.ZERO));
				}
				//挖矿奖励
				if (null == transaction.getFrom()) {
					recipient.get().setBalance(recipient.get().getBalance().add(transaction.getAmount()));
					dbAccess.putAccount(recipient.get());
					continue;
				}
				//账户转账
				Optional<Account> sender = dbAccess.getAccount(transaction.getFrom());
				//验证签名
				boolean verify = Sign.verify(
						Keys.publicKeyDecode(transaction.getPublicKey()),
						transaction.getSign(),
						transaction.toString());
				if (!verify) {
					transaction.setStatus(TransactionStatusEnum.FAIL);
					transaction.setErrorMessage("交易签名错误");
					continue;
				}
				//验证账户余额
				if (sender.get().getBalance().compareTo(transaction.getAmount()) == -1) {
					transaction.setStatus(TransactionStatusEnum.FAIL);
					transaction.setErrorMessage("账户余额不足");
					continue;
				}

				//执行转账操作,更新账户余额
				sender.get().setBalance(sender.get().getBalance().subtract(transaction.getAmount()));
				recipient.get().setBalance(recipient.get().getBalance().add(transaction.getAmount()));
				dbAccess.putAccount(sender.get());
				dbAccess.putAccount(recipient.get());
			}//end synchronize
		}// end for

		//清空交易池
		transactionPool.clearTransactions();
	}
}

  TransactionPool.java

package com.ppblock.blockchain.core;

import com.google.common.base.Objects;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * 交易池
 * @author yangjian
 * @since 18-4-23
 */
@Component
public class TransactionPool {

	private List<Transaction> transactions = new ArrayList<>();


	/**
	 * 添加交易
	 * @param transaction
	 */
	public void addTransaction(Transaction transaction) {

		boolean exists = false;
		//检验交易是否存在
		for (Transaction tx : this.transactions) {
			if (Objects.equal(tx.getTxHash(), transaction.getTxHash())) {
				exists = true;
			}
		}
		if (!exists) {
			this.transactions.add(transaction);
		}
	}

	public List<Transaction> getTransactions() {
		return transactions;
	}

	/**
	 * 清空交易池
	 */
	public void clearTransactions() {
		this.transactions.clear();
	}

}

  

推荐阅读