文章目录
学习过程
Python开发区块链
1 第1课
视频:https://www.bilibili.com/video/BV1RZ4y1c7i2/
代码:https://github.com/gjhuai/blockchain
1.1 比特币
1.1.1 比特币-起源
- Bitcoin(BTC): A Peer-to-Peer Electronic Cash System( 点对点电子现金系统 )
- 中本聪在 2009 年初挖出第一批比特币
- 总量不超过 2100 万枚
1.1.2 比特币-底层机制
- 去中心化, P2P 分布式数字货币系统
- 共识机制 -POW 工作量证明
- 非对称加密算法 - 数字签名
- 区块链账本技术
1.1.3 比特币-特性
- 硬通货 - 跨境交易
- 易携带 - 只需一个私钥
- 隐秘性 - 只暴露钱包地址
- 无货币超发
1.1.4 比特币-钱包
- 钱包就是 P2P 里的 P( 节点 ) ,主要用来管理私钥和比特币转账地 址
- 钱包分类
- 轻钱包 - 只存储维护跟你自己交易相关的数据
- 中心化钱包 - 数字货币交易所
- 钱包下载地址: https://bitcoin.org/zh_CN/download
1.1.5 比特币-产生
- 比特币由矿工挖矿产生
- 生成的比特币被记录在矿工的名下
- 比特币通过矿工的公钥哈希值锁定
- 比特币通过交易 (UTXO) 在节点之间转移
- UTXO- 未花费的交易
1.2 区块链
1.2.1 区块链-分布式账本系统
- 共识机制 -POW 工作量证明
- 通过挖矿证明自己是善意节点,并获得生成区块和在该区块记账的权利
- 基于 P2P 网络,每个全节点都存储着最全的比特币交易记录
- 新区块通过包含前一个区块头部的哈希值 ( 区块的唯一标识 ) 建立链接关系
- 区块里装的就是所有的比特币交易记录 (UTXO)
1.2.2 区块链网络
- 区块链节点
- 矿工-运行于强大或专用的硬件(比如 ASIC)之上,主要目的是挖矿
- 全节点-这些节点验证矿工挖出来的块的有效性,并对交易进行确认。
- SPV节点-简单支付验证,如钱包节点
1.2.3 区块链-挖矿
- 在全网中和其他节点竞争计算 ( 解一个难题 ) 的过程
- 证明自己是非恶意节点
- 获得的权利和义务
- 记账权 - 把交易写入区块里
- 广播义务 - 把区块在全网广播
- 获得的奖励
- 挖矿奖励 -12.5BTC
- 收取交易手续费
1.2.4 区块链-共识机制
-
拜占庭将军问题 - 共识机制之一
-
POW(Proof of Work)-工作量证明
- 通过付出大量工作代价证明自己是非恶意节点
- 计算一个随机数 (nonce) ,算出的正确随机数即 POW
- 获取记账权利
- 打包交易并通知其它节点
-
理性人都是逐利的, POW 抑制了节点的恶意动机
1.2.5 区块链-交易确认
- 当一项交易被链上的区块收录后,就是交易确认
- 在此区块之后每产生一个区块,此项交易的确认数相应加 1
- 经过 6 个以上区块确认的交易才是安全确认的,因为篡改成本巨大
- 比特币钱包可以设置交易确认数
1.2.6 区块链-区块生成
-
矿工在挖矿前要组建区块
- 将 coinbase 交易打包进区块
- 将交易池中高优先级的交易打包进区块
- 创建区块头部
-
挖矿成功后,将计算出来的随机数 nonce(POW) 填入区块头部, 并向临近节点传播
1.2.7 区块链-区块验证
- 相邻节点收到新区块后,立即做以下验证
- 验证 POW 的 nonce 值是否符合难度值
- 检查时间戳是否小于当前时间两小时
- 检查 merkle 树根是否正确
- 检查区块 size 要小于区块 size 的上限
- 第一笔交易必须是 coinbase 交易
- 验证每个交易
1.2.8 区块链-分类
- 公有链
- 任何人都可以参与使用和维护,信息公开,如比特币,以太坊等
- 联盟链
- 若干组织共同维护,使用有权限限制,信息受保护,如银联组织
- 私有链
- 集中管理者进行限制,内部少数人可以使用,信息不公开
1.2.9 区块链-篡改账本
- 双花问题:同一笔比特币被支付多次
1.3 密码学
1.3.1 密码学-对称加密
- 对称加密 - 加解密钥相同
- 缺点:无法确保密钥被安全传递
- 常用算法: DES 、 3DES ( TripleDES )、 AES 等
1.3.2 密码学-非对称加密
- 非对称加密 - 公私钥加密对,公钥加密,私钥解密
- 公钥由私钥生成,私钥可以推导出公钥
- 从公钥无法推导出私钥
- 优点:解决了密钥传输中的安全行问题
- 常用算法: RSA 、 ECC (椭圆曲线加密算法 )
- 使用场景: SSH 安全验证等
- 问题:解决了信息传送的问题,如何验证发送方是正确的了 ?
1.3.3 密码学-哈希(Hash)
- 哈希 - 将一段数据 ( 任意长度 ) 经过计算转换成一段定长的数据
- 不可逆性 - 几乎无法通过哈希的结果推导出原文
- 无碰撞性 - 两个不同原文哈希后的结果一定不同
- 常用算法: MD5 , SHA256
- 使用场景
- 数据库中的用户密码存储 (MD5)
- 挖矿计算 (SHA256)
1.3.4 密码学-数字签名
- 数字签名 - 公私钥加密对,私钥签名,公钥解签名
- 使用场景 - 比特币交易验证等
1.3.5 Java实现区块链与比特币
- 区块链结构
- 挖矿生成新区块
- 共识机制
- 比特币交易
- 比特币钱包
- 区块链 P2P 网络通讯
比特派
第2课
- 区块链整体结构设计与实现
- 共识机制
-
工作量证明原理
-
挖矿算法
-
// 挖矿算法
//创建一个空的区块链
List<Block> blockchain = new ArrayList<>();
//生成创世区块
Block block = new Block(1, System.currentTimeMillis(), new ArrayList<Transaction>(), 1, "1", "1");
//加入创世区块到区块链里
blockchain.add(block);
System.out.println(JSON.toJSONString(blockchain));
//创建一个空的交易结合
List<Transaction> txs = new ArrayList<>();
Transaction tx1 = new Transaction();
Transaction tx2 = new Transaction();
Transaction tx3 = new Transaction();
txs.add(tx1);
txs.add(tx2);
txs.add(tx3);
//加入系统奖励的交易
Transaction sysTx = new Transaction();
txs.add(sysTx);
//获取当前区块链里的最后一个区块
Block latestBlock = blockchain.get(blockchain.size() - 1);
int nonce = 1;
String hash = "";
while(true){
hash = CryptoUtil.SHA256(latestBlock.getHash() + JSON.toJSONString(txs) + nonce);
if (hash.startsWith("0000")) {
System.out.println("=====计算结果正确,计算次数为:" +nonce+ ",hash:" + hash);
break;
}
nonce++;
System.out.println("计算错误,hash:" + hash);
}
Block newBlock = new Block(latestBlock.getIndex() + 1, System.currentTimeMillis(), txs, nonce, latestBlock.getHash(), hash);
blockchain.add(newBlock);
System.out.println("挖矿后的区块链:" + JSON.toJSONString(blockchain));
密码学–非对称加密
- 非对称加密–公私钥加密对,共要加密,私钥解密
- 公钥由私钥生成,私钥可以推到出公钥
- 从公钥无法推导出私钥
- 优点:解决了密钥传输中的安全问题
- 常用算法:RSA、ECC(椭圆曲线加密算法)
- 使用场景:SSH安全验证等
- 问题:解决了信息传送的问题,如何验证发送方是正确的?
@Test
public void testEncrypt() throws Exception {
System.err.println("公钥加密——私钥解密");
String inputStr = "abc";
byte[] data = inputStr.getBytes();
byte[] encodedData = RSACoder.encryptByPublicKey(data, publicKey);
byte[] decodedData = RSACoder.decryptByPrivateKey(encodedData, privateKey);
String outputStr = new String(decodedData);
System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
assertEquals(inputStr, outputStr);
}
密码学–哈希(Hash)
- 哈希–讲一段数据(任意长度)经过计算转换成一段定长的数据
- 不可逆性–几乎无法通过哈希的结果推导出原文
- 无碰撞行–两个不同原文哈希后的结果一定不同
- 常用算法:MD5、SHA256
- 使用场景:
- 数据库中的用户密码存储(MD5)
- 挖矿计算(SHA256)
密码学–数字签名
1、数字签名:公私钥加密对,私钥签名,公钥验证签名。
2、使用场景:比特币交易验证等。
@Test
public void testSign() throws Exception {
System.err.println("私钥签名——公钥验证签名");
String inputStr = "sign";
byte[] data = inputStr.getBytes();
// 产生签名
String sign = RSACoder.sign(data, privateKey);
System.err.println("签名:\r" + sign);
// 验证签名
boolean status = RSACoder.verify(data, publicKey, sign);
System.err.println("状态:\r" + status);
assertTrue(status);
}
生成钱包
// 本地生成公私钥对
Map<String, Object> initKey = RSACoder.initKey();
String publicKey = RSACoder.getPublicKey(initKey);
String privateKey = RSACoder.getPrivateKey(initKey);
return new Wallet(publicKey, privateKey);
3 第3课:比特币的设计与实现
比特币交易UTXO
- 交易输出
- 交易输入
比特币余额
3.1 比特币–UTXO
- UTXO(unspent transaction output) - 未花费交易输出
- 比特币拥有者的公钥锁定(加密)的一个数字
- UTXO就是比特币,比特币系统中只有UTXO,没有比特币
- 新的UTXO由挖矿或交易产生
- UTXO存在全节点的数据库里
- 转账交易消耗自己的UTXO,同时生成新的UTXO,并用接受者的公钥锁定
- 比特币系统中用户的“余额”实际上并不直接存在,而是通过计算得来
3.2 比特币–交易模型
- 交易输出(UTXO)
- 锁定的比特币数量
- 锁定脚本(用接收者的公钥哈希)
- 交易输入(UTXO+解锁脚本)
- 解锁脚本(发送者的签名和公钥)
- 签名–对发送者和接收者的公钥哈希以及整个交易签名
3.2 比特币–交易全流程
coinbase的系统交易,input为空。
3.3 交易代码
//交易发起方
Wallet walletSender = Wallet.generateWallet();
//交易接收方
Wallet walletReciptent = Wallet.generateWallet();
TransactionInput txIn = new TransactionInput(tx2.getId(), 10, null, walletSender.getPublicKey());
TransactionOutput txOut = new TransactionOutput(10, walletReciptent.getHashPubKey());
Transaction tx3 = new Transaction(CryptoUtil.UUID(), txIn , txOut);
//假定tx2之前已经被打包进区块,也就是已经被记录进账本了
tx3.sign(walletSender.getPrivateKey(), tx2);
txs.add(tx3);
3.4 签名代码
对发送者和接收者的公钥哈希以及整个交易签名
/**
* 用私钥生成交易签名
*
* @param privateKey
* @param prevTx
*/
public void sign(String privateKey, Transaction prevTx) {
if (coinbaseTx()) {
return;
}
if (!prevTx.getId().equals(txIn.getTxId())) {
System.err.println("交易签名失败:当前交易输入引用的前一笔交易与传入的前一笔交易不匹配");
}
Transaction txClone = cloneTx();
txClone.getTxIn().setPublicKey(prevTx.getTxOut().getPublicKeyHash());
String sign = "";
try {
sign = RSACoder.sign(txClone.hash().getBytes(), privateKey);
} catch (Exception e) {
e.printStackTrace();
}
txIn.setSignature(sign);
}
/**
* 生成用于交易签名的交易记录副本
*
* @return
*/
public Transaction cloneTx() {
TransactionInput transactionInput = new TransactionInput(txIn.getTxId(), txIn.getValue(), null, null);
TransactionOutput transactionOutput = new TransactionOutput(txOut.getValue(), txOut.getPublicKeyHash());
return new Transaction(id, transactionInput, transactionOutput);
}