本篇文章将向同学们介绍区块链相关知识, 以及如何使用Java实现一个基于联盟链的区块链系统, 你甚至可以发布属于自己的虚拟货币.
私有链对单独的个人或实体开放,仅在私有组织,比如公司内部使用,私有链上的读写权限,参与记账的权限都由私有组织来制定。
再举个例子,和刚刚每个人都可以进入到虚拟世界不同,私有链就好比是你自己的家,这个家是你个人拥有的,是你私有的东西,因此你想让谁进出你家(权限),想让谁动(记账)你家东西就是完全归你自己决定。因为,进出家的人都是自己控制的,所以可以防止他人的恶意***,更好的保护自己的隐私,提高与他人的交易速度。
通过上面的例子,我们可以看出相比公有链去中心化数据库,私有链能够防止机构内单节点故意隐瞒或者篡改数据因此私有链往往可以有极快的交易速度、更好的隐私保护、更低的交易成本、不容易被恶意***。
公有链是指全世界任何人可以读取、发送交易却能获得有效确认的共识区块链。也就是说,公有链上的行为是公开透明的,不受任何人控制,也不受任何人所有,是“完全去中心化”的区块链。
举个例子来说,假设公有链是一个虚拟世界,那么我们任何人都可以进入这个虚拟世界去构建自己的家园(也就是“挖矿”)或者和他人进行交易,而这个虚拟世界保证了交易的安全性和不可篡改性
而公有链因为人人可参与,无需授权的特点又被称为非许可链,即不需要验证身份即可参与一切活动。就好比这个虚拟世界,人人都可以在这里建设家园(也就是“挖矿”)。
完全去中心化任何人都可以参与,门槛低任何拥有技术的人都可以访问,只需要一台能联网的计算机就能够满足访问条件。所有数据默认公开因为是完全去中心化的,所以任何人都可以看到他人的信息。
由于建设虚拟家园的人会越来越多,大规模的消耗资源(比如挖矿耗电),虚拟世界的负荷也会越来越重,导致效率变低,想要建成楼房(验证和完成)就需要较长的时间。所以由于技术条件不成熟,交易量一旦过多容易造成网络拥堵,导致效率变低,这也是很多公链需要去解决的痛点。公有链的运用:有比特币、以太坊、超级账本、大多数山寨币以及智能合约等。
适用于数字货币、电子商务、互联网金融、知识产权等。
因为需要保密的行业和应用也不需要公有链公开透明的特性,联盟链应运而生。是指有若干个机构共同参与管理的区块链,每个机构都运行着一个或多个节点,其中的数据只允许系统内不同的机构进行读写和发送交易,并且共同来记录交易数据。所以联盟链上的读写权限、以及记账规则都按联盟规则来“私人定制”。
让我再来和你们举个例子:
我们都知道一个集团是由很多不同的部门构建而成,因此公司的走向是由各个部门合作共同决定的(联盟的规则共同决定),而各个部门都有着自己的职责(不同的节点),各个部门都不能跨部门工作,所以各自书写自己的数据信息,发放到一个指定文件夹,然后这个指定文件是公开透明的的每个部门都可以下载浏览。当有别的公司访问的时候会有一个专门的部门接待(该区块链开放的API进行限定查询)。
联盟链由参与成员机构共同维护,并提供了对参与成员的管理、认证、授权、监控、审计等全套安全管理功能。2015年成立的R3联盟,就是银行业的一个联盟链,目前已加入的成员多达40多个,包括世界著名的银行摩根大通、汇丰、高盛等。
下表总结了一些主要差异
所谓“合约”,就是条文、合同一类的东西,里面记录了发生的条件与对应执行的条款,以支持确权等操作;所谓"智能",就意味着自动化、可编程。
所以,智能合约就是可编程的合同,也可以理解为一段自动执行的条文合同,在计算机中,就是一段自动执行的程序片段。它更易于合约保存,并且由确定的算法运行,给定输入,就得到对应的输出,极大保障了合约的执行力。
以自动售货机做类比,可以帮助我们更好地理解智能合约的核心特征。
当使用者选择好要购买的货物并完成支付,出货逻辑就会被触发,用户就能得到想要的货物,而这个过程不需要人工介入,节省了售卖货物的人力成本。如果要破坏这个合约,就得物理破坏售卖机。像 POS 刷卡机、EDI(电子数据交换)等,也可作此种类比。
下面将分享一个基础的智能合约:HelloWorld。
这段 Solidity 代码的功能是存取 _num 字段。该字段被称为“状态变量”,会由区块链持久存储。
用户可以将这段代码部署在以太坊或类似的区块链上,部署成功就意味着该智能合约不可再被修改,只要底层区块链不被销毁,这段合约就一直存在。任何人都可通过“合约地址”来调用该合约接口,每次调用信息都会被记录在链上。
在讲解这段代码如何运行之前,我们先回顾下传统 java 程序的运行方式。
首先,用户编译完 java 代码后,会以字节码的形式保存在磁盘上;然后用户会调用程序,这由 JVM 来托管执行;程序执行期间可能会通过日志来记录调用参数,也可能会和磁盘进行 IO。
Solidity 的执行与此类似。不同的是介质由硬盘换成了区块链,由单机变为分布式。
代码部署后,以字节码的形式存储在每一个节点上。当用户要求调用某个函数时,调用请求将会被囊括在交易中,并被打包到某个区块上,一旦全网对该区块形成共识,就意味着调用是合法的。
接下来,EVM 会来调用字节码,它负责存取底层的状态变量,好比传统编程的 IO。
光从代码来看,合约开发似乎不过如此,单个合约只需要围绕着字段进行操作,对于很多简单业务而言,不过是 CRUD 而已。
但其复杂性也恰恰在于此,合约在区块链环境上执行,是不可修改的。
所以如果出现了 bug,就必须部署新的合约,这对于合约的可维护性提出了挑战。并且,一旦业务复杂起来,容易出现安全漏洞,导致链上资产损失。同时,还要考虑完成代码编写、逻辑执行、数据存储的成本问题。
共识算法可以被定义为一个通过区块链网络达成共识的机制。公共的(去中心化的)区块链作为一个分布式系统,并不依赖于一个中央机构,而是由分布式节点全票通过来实现交易。与此同时,共识算法开始发挥作用,它保证了协议规则的正常执行以及交易可以在免信任情况下发生,因此所有的数字都货币只能被消费一次。
由于现有的共识算非常多, 这里列举几个供大家参考, 有需要的可以自查或者找学长要
无论区块数据还是状态数据,它们都是由区块链节点使用和存储的。区块链节点是一个程序,运行在我们的个人电脑、虚拟机或服务器上。多个分布在不同电脑或服务器上的区块链节点,通过网络互相连接,组成了完整的区块链网络。
区块链节点通常会把区块链数据存储在个人电脑、虚拟机或服务器上,存储区块链数据最常见的介质,就是磁盘。
区块链节点不会直接访问磁盘,它们会通过特定的数据库,如 LevelDB、RocksDB 或 MySQL 等单机或分布式数据库来操作数据。相比于直接操作磁盘,数据库抽象了特定的数据访问模型,对区块链节点更为友好。
因此,当我们说:“区块链数据保存在数据库”时,可以认为区块链节点将区块链数据保存在 MySQL(或其它数据库),MySQL 将区块链数据保存在磁盘。
区块是区块链系统的最小单元,第一步我们先实现最简单的区块结构,新建Block.java类,主要包含以下几个字段:
Block.java
/**
* 区块结构
*
* @author Jared Jia
*
*/
public class Block implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 区块索引号(区块高度)
*/
private int index;
/**
* 当前区块的hash值,区块唯一标识
*/
private String hash;
/**
* 前一个区块的hash值
*/
private String previousHash;
/**
* 生成区块的时间戳
*/
private long timestamp;
/**
* 工作量证明,计算正确hash值的次数
*/
private int nonce;
/**
* 当前区块存储的业务数据集合(例如转账交易信息、票据信息、合同信息等)
*/
private List<Transaction> transactions;
/*** 省略get set方法****/
}
区块链是由区块按照区块哈希前后顺序串联起来的数据结构,哈希值通过散列算法对区块进行二次哈希计算而得到的数字摘要信息(不了解散列函数的,可以先百度了解一下SHA算法),用于保证区块的信息安全以及整条区块链的有效性。因此第二步我们新增计算区块Hash值的方法,采用SHA256算法,通过java实现:
CryptoUtil.java
/**
* 密码学工具类
*
* @author Jared Jia
*
*/
public class CryptoUtil {
/**
* SHA256散列函数
* @param str
* @return
*/
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;
}
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();
}
}
第三步,创建一个链式结构对象,按照先后顺序来保存区块对象,从来形成一个有序的区块链表,考虑到线程安全问题,采用CopyOnWriteArrayList来实现,为了方便测试,暂且把区块链结构保存在本地缓存中,实际的区块链网络最终会实现持久层的功能,把区块链数据保存至数据库中,例如BTC核心网络采用的是K-V数据库LevelDB:
BlockCache.java
public class BlockCache {
/**
* 当前节点的区块链结构
*/
private List<Block> blockChain = new CopyOnWriteArrayList<Block>();
public List<Block> getBlockChain() {
return blockChain;
}
public void setBlockChain(List<Block> blockChain) {
this.blockChain = blockChain;
}
}
第四步,有了区块链结构后,需要新增向区块链中添加区块的方法,同时每次添加区块的时候,我们需要验证新区块的有效性,例如Hash值是否正确,新区块中上一区块的Hash属性的值,与上一区块的Hash值是否相等。
另外,区块链中必须有个创世区块,我们直接通过硬编码实现:
BlockService.java
/**
* 区块链核心服务
*
* @author Jared Jia
*
*/
@Service
public class BlockService {
@Autowired
BlockCache blockCache;
/**
* 创建创世区块
* @return
*/
public String createGenesisBlock() {
Block genesisBlock = new Block();
//设置创世区块高度为1
genesisBlock.setIndex(1);
genesisBlock.setTimestamp(System.currentTimeMillis());
genesisBlock.setNonce(1);
//封装业务数据
List<Transaction> tsaList = new ArrayList<Transaction>();
Transaction tsa = new Transaction();
tsa.setId("1");
tsa.setBusinessInfo("这是创世区块");
tsaList.add(tsa);
Transaction tsa2 = new Transaction();
tsa2.setId("2");
tsa2.setBusinessInfo("区块链高度为:1");
tsaList.add(tsa2);
genesisBlock.setTransactions(tsaList);
//设置创世区块的hash值
genesisBlock.setHash(calculateHash("",tsaList,1));
//添加到已打包保存的业务数据集合中
blockCache.getPackedTransactions().addAll(tsaList);
//添加到区块链中
blockCache.getBlockChain().add(genesisBlock);
return JSON.toJSONString(genesisBlock);
}
/**
* 创建新区块
* @param nonce
* @param previousHash
* @param hash
* @param blockTxs
* @return
*/
public Block createNewBlock(int nonce, String previousHash, String hash, List<Transaction> blockTxs) {
Block block = new Block();
block.setIndex(blockCache.getBlockChain().size() + 1);
//时间戳
block.setTimestamp(System.currentTimeMillis());
block.setTransactions(blockTxs);
//工作量证明,计算正确hash值的次数
block.setNonce(nonce);
//上一区块的哈希
block.setPreviousHash(previousHash);
//当前区块的哈希
block.setHash(hash);
if (addBlock(block)) {
return block;
}
return null;
}
/**
* 添加新区块到当前节点的区块链中
*
* @param newBlock
*/
public boolean addBlock(Block newBlock) {
//先对新区块的合法性进行校验
if (isValidNewBlock(newBlock, blockCache.getLatestBlock())) {
blockCache.getBlockChain().add(newBlock);
// 新区块的业务数据需要加入到已打包的业务数据集合里去
blockCache.getPackedTransactions().addAll(newBlock.getTransactions());
return true;
}
return false;
}
/**
* 验证新区块是否有效
*
* @param newBlock
* @param previousBlock
* @return
*/
public boolean isValidNewBlock(Block newBlock, Block previousBlock) {
if (!previousBlock.getHash().equals(newBlock.getPreviousHash())) {
System.out.println("新区块的前一个区块hash验证不通过");
return false;
} else {
// 验证新区块hash值的正确性
String hash = calculateHash(newBlock.getPreviousHash(), newBlock.getTransactions(), newBlock.getNonce());
if (!hash.equals(newBlock.getHash())) {
System.out.println("新区块的hash无效: " + hash + " " + newBlock.getHash());
return false;
}
if (!isValidHash(newBlock.getHash())) {
return false;
}
}
return true;
}
}
挖到的第一块虚拟货币, 丹成币
原文:https://blog.51cto.com/14974381/2546738