JAVA版本区块链钱包核心代码
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(); } }