通过创建区块链来学习区块链(1)

主要内容:

  1. Block的数据结构
  2. 挖矿的过程
  3. 工作量证明机制
  4. 工作量证明的简单实现
  5. 建立一条简单的测试链

环境:

  • python 3.7

区块链本质上是一个共享数据库,存储于其中的数据或信息,具有如下特点:

  • 数据仅可通过共识算法以块的形式增加,不可修改或删除,以防止篡改。
  • 每个区块至少会包含一个块生成时间和出块签名。
  • 所有的交易数据都会被双方签名,以防止抵赖。
  • 传统区块链中,新增区块中储存上一个区块的hash,并通过此hash与上一个区块相连。

区块(Block)长什么样

Block 包含下面几个要素:

  • index : 索引
  • timestamp :时间戳
  • transactions : 交易列表
  • proof : 证明(工作量证明)通过工作量证明算法计算
  • previous_hash :上一个区块的哈希值,保证了区块链的不变性
block = {'index': 1,'timestamp': 1626857298.6391933,'transactions': [{'sender': "147FA8527",'recipient': "DA5DF1FD8",'amount': 50,}],'proof': 32740,'previous_hash': "06e825e27e52f558e92f7be2f679caf101fbfe227e9cd8d80845029911a0f2d4"
}

class Blockchain

创建一个Blockchain类用来初始化区块链实例。

class Blockchain :

  • 属性:

    • chain : 列表记录Block
    • transaction : 列表记录交易
  • 方法:
    • new_block() : 创建新的区块(Block)并加入链中(chain)
    • new_transaction() : 添加新的交易(transaction)到交易记录中,并返回下一个区块的索引
    • hash() : 生成区块的哈希值
    • last_block() : 返回区块链中的最后一个块

方法的具体实现:

class Blockchain(object):def __init__(self):self.chain = []self.current_transactions = []# 初始化实例时,同时也创建一个头节点self.new_block(previous_hash = 666, proof = 888)def new_block(self, previous_hash, proof):# 创建一个新的block并添加到chain中"""previous_hash : <int>/<str> 前一个block的哈希值proof :<int> 工作量证明return -> <dict> New Block"""block = {}block['index'] = len(self.chain) + 1block['timestamp'] = time()block['transactions'] = self.current_transactionsblock['proof'] = proofblock['previous_hash'] = previous_hash or self.hash(self.chain[-1])self.chain.append(block)## clean current_transactionsself.current_transactions = []return blockdef new_transaction(self, sender, recipient, amount):# 添加新的transaction到交易列表中"""sender : <str> sender的地址recipient : <str> recipient的地址amount : <int> 数量return -> <int> 索引,是当前block的后续block的索引"""transaction = {}transaction['sender'] = sendertransaction['recipient'] = recipienttransaction['amount'] = amountself.current_transactions.append(transaction)return self.last_block['index'] + 1@staticmethoddef hash(block):# 生成block的哈希值"""block : <dict> blockreturn -> <str> 哈希值"""block_string = json.dumps(block, sort_keys=True).encode()hash_val = hashlib.sha256(block_string).hexdigest()return hash_val@propertydef last_block(self):# 返回chain中的最后一个blockreturn self.chain[-1]

​   Blockchain类已经实现了创建区块链的大部分功能,但是还差一个很重要的功能,那就是工作量计算和验证。从new_block()可以看出,每创建一个新的区块除了要记录一个不定长的交易列表外,还要提供一个工作量证明。了解工作量证明之前先简单说一下挖矿。

矿是怎么挖的

​  挖矿就是创建新的区块并添加到链上的一个过程,每当一个矿工成功创建一个区块就会获得一些加密货币作为奖励。但是在整个网络中同时有成千上万甚至几十万的矿工在挖矿,奖励有限,给谁?为了公平起见,采用了一种工作量证明机制,每个矿工在创建新区块的时候,要提供工作量证明,工作量被认可,矿工创建的区块才会被认可,并获得奖励。同时会立刻向全网广播该区块。其他矿工在收到新区块后,会对新区块进行验证,如果有效,就把它添加到区块链的尾部。在本轮工作量证明的竞争中,这个矿工胜出,而其他矿工都失败了。失败的矿工会抛弃自己当前正在计算还没有算完的区块,转而开始计算下一个区块,进行下一轮工作量证明的竞争。

工作量怎么证明的

工作量证明的本质就是计算,通过计算证明矿工的工作量,工作量证明有两个特点:

  1. 计算过程十分复杂
  2. 工作量的验证过程简单

​   哈希算法常被用来进行工作量证明计算,矿工就是通过不断的尝试哈希计算,直到找到了一个满足要求的哈希值,此时就算是挖矿成功了。具体的工作量证明不同的区块链略有不同,但本质都是进行哈希计算。

下面通过一个简单的例子来说明一下:

​   假设:当前网络中对创建新区块的要求是:哈希值的首位必须为0。为了尝试不同的哈希值,在挖矿过程中,我们采用一个变量PPP,跟前一个区块一起进行哈希计算(为了简单起见,我们只用上一个区块中的工作量证明参与哈希计算),直到得到特定的哈希值,此时那个PPP就可以作为矿工的工作量证明,否则不停的尝试新的PPP。验证的方法就是,将矿工提供工作量证明PPP跟前一个区块的工作量证明组合在一起进行哈希计算,检验哈希值是否满足要求。

举个例子,当前链中最后一个Block的工作量证明 proof = 888,下一个区块的工作量证明过程如下:

from hashlib import sha256prev_proof = 888
new_proof = 0    # ? P
count = 0        # 记录hash的次数
while sha256(f'{prev_proof}{new_proof}'.encode()).hexdigest()[0] != "0":  # 哈希值的首字符为0new_proof = new_proof + 2count += 1
print("count : %d, new_proof : %d"%(count, new_proof))
count : 13, new_proof : 26

经过13次的计算,就找到的满足的哈希值和新的工作量证明,下面验证工作量是否有效:

new_proof = 26sha256(f'{prev_proof}{new_proof}'.encode()).hexdigest()
>>>'06e825e27e52f558e92f7be2f679caf101fbfe227e9cd8d80845029911a0f2d4'

经过验证,工作量有效。下面继续模拟上面的过程,假设当前的要求是前两位哈希值为00:

prev_proof = 26
new_proof = 0    # ? P
count = 0        # 记录hash的次数
while sha256(f'{prev_proof}{new_proof}'.encode()).hexdigest()[:2] != "00":new_proof = new_proof + 2count += 1
print("count : %d, new_proof : %d"%(count, new_proof))
count : 25, new_proof : 50

前三位哈希值为000:

prev_proof = 50
new_proof = 0    # ? P
count = 0        # 记录hash的次数
while sha256(f'{prev_proof}{new_proof}'.encode()).hexdigest()[:3] != "000":new_proof = new_proof + 2count += 1
print("count : %d, new_proof : %d"%(count, new_proof))
count : 1266, new_proof : 2532

前四位哈希值为0000:

prev_proof = 2532
new_proof = 0    # ? P
count = 0        # 记录hash的次数
while sha256(f'{prev_proof}{new_proof}'.encode()).hexdigest()[:4] != "0000":new_proof = new_proof + 2count += 1
print("count : %d, new_proof : %d"%(count, new_proof))
count : 6280, new_proof : 12560

前五位哈希值为00000

prev_proof = 12560
new_proof = 0    # ? P
count = 0        # 记录hash的次数
while sha256(f'{prev_proof}{new_proof}'.encode()).hexdigest()[:5] != "00000":new_proof = new_proof + 2count += 1
print("count : %d, new_proof : %d"%(count, new_proof))
count : 356128, new_proof : 712256

前六位哈希值为000000:

prev_proof = 712256
new_proof = 0    # ? P
count = 0        # 记录hash的次数
while sha256(f'{prev_proof}{new_proof}'.encode()).hexdigest()[:6] != "000000":new_proof = new_proof + 2count += 1
print("count : %d, new_proof : %d"%(count, new_proof))
count : 1552855, new_proof : 3105710

​  从上面的模拟结果可以发现,随着网络对创建新区块的哈希值要求的变化,矿工要进行哈希计算的次数也在迅速增加,这意味着挖矿的成本也在增加。实际挖矿中,这个挖矿的难度,即哈希值前几位为0的个数会动态的调整,来保证新区块生成的效率。全网算力减少(矿工减少),难度值就会减少,反之算力增加,挖矿难度也随之增加。

工作量证明实现和验证

class Blockchain(object):def __init__(self): ...def new_block(self, previous_hash, proof): ...def new_transaction(self, sender, recipient, amount): ...@staticmethoddef hash(block):...@propertydef last_block(self): ...def proof_of_work(self, last_proof):"""POW : hash(pp')p : previous_proof, p': new_prooflast_proof: <int> 上一个区块中的proofreturn -> <int> new_proof 满足哈希条件的工作量证明"""proof = 0while self.valid_proof(last_proof, proof) is False:proof += 1return proof@staticmethoddef valid_proof(last_proof, proof):"""验证POW,hash(pp')是否满足条件0000"""guess = f'{last_proof}{proof}'.encode()hash_val = hashlib.sha256(guess).hexdigest()return hash_val[:4] == "0000"

创建一条测试链

test_chain = Blockchain()
# 链中的第一个block
test_chain.chain
[{'index': 1,'timestamp': 1626857298.6391933,'transactions': [],'proof': 888,'previous_hash': 666}]
# 记录网络中一段时间里发生的交易
test_chain.new_transaction("APPLES123","BANANA234", 111)
test_chain.new_transaction("JDI83NG0C","BANANA234", 123)
test_chain.new_transaction("APPLES123","BANANA234", 345)
test_chain.new_transaction("JDI83NG0C","BANANA234", 666)

工作量证明

# 当前链中最后一个block的工作量证明
prev_proof = test_chain.last_block['proof']
# 工作量证明的计算,返回验证通过的工作量证明
new_proof = test_chain.proof_of_work(prev_proof)

创建新的区块

# 计算链中最后一个block的哈希值
prev_hash = test_chain.hash(test_chain.last_block)
# 创建新区块并加入链中
test_chain.new_block(prev_hash, new_proof)
{'index': 2,'timestamp': 1626857779.4037974,'transactions': [{'sender': 'APPLES123','recipient': 'BANANA234','amount': 111},{'sender': 'JDI83NG0C', 'recipient': 'BANANA234', 'amount': 123},{'sender': 'APPLES123', 'recipient': 'BANANA234', 'amount': 345},{'sender': 'JDI83NG0C', 'recipient': 'BANANA234', 'amount': 666}],'proof': 124179,'previous_hash': '3c339eac60c9a4f1ee84d42a5c70bea4d510a57a80d833a2cbfc849768ec0964'}
继续添加新的区块
# 记录交易
test_chain.new_transaction("TNCSSS256","JWDLH021", 111)
# 工作量证明
prev_proof = test_chain.last_block['proof']
new_proof = test_chain.proof_of_work(prev_proof)
# 创建新区块
prev_hash = test_chain.hash(test_chain.last_block)
test_chain.new_block(prev_hash, new_proof)
{'index': 3,'timestamp': 1626858267.9535542,'transactions': [{'sender': 'TNCSSS256','recipient': 'JWDLH021','amount': 111}],'proof': 39221,'previous_hash': '0d411a993912d9e20e1ccdc670ab1b8ae2062973c5c9eddca8c47a4ad3d3c9b0'}
test_chain.chain
[{'index': 1,'timestamp': 1626857298.6391933,'transactions': [],'proof': 888,'previous_hash': 666},{'index': 2,'timestamp': 1626857779.4037974,'transactions': [{'sender': 'APPLES123','recipient': 'BANANA234','amount': 111},{'sender': 'JDI83NG0C', 'recipient': 'BANANA234', 'amount': 123},{'sender': 'APPLES123', 'recipient': 'BANANA234', 'amount': 345},{'sender': 'JDI83NG0C', 'recipient': 'BANANA234', 'amount': 666}],'proof': 124179,'previous_hash': '3c339eac60c9a4f1ee84d42a5c70bea4d510a57a80d833a2cbfc849768ec0964'},{'index': 3,'timestamp': 1626858267.9535542,'transactions': [{'sender': 'TNCSSS256','recipient': 'JWDLH021','amount': 111}],'proof': 39221,'previous_hash': '0d411a993912d9e20e1ccdc670ab1b8ae2062973c5c9eddca8c47a4ad3d3c9b0'}]

下一步:

  • 创建一个web服务实现矿工节点跟区块链间的通信
  • 模拟矿工跟区块链间的通信过程

通过创建一条链来学习区块链 (1)相关推荐

  1. 通过python构建一个区块链来学习区块链

    了解区块链Blockchains如何工作的最快方法就是构建一个区块链.你来到这里是因为,和我一样,你对加密钱币的崛起感到很兴奋.而且你想知道区块链是如何工作的,想了解它们背后的基本技术. 但理解区块链 ...

  2. 学习区块链要掌握哪些专项能力?区块链学习培训多长时间?

    2017年在北京举办的"一带一路"国际合作高峰论坛,沿线的20国青年评选出了他们心目中中国的"新四大发明":高铁.支付宝.共享单车和网购.而在这"新四 ...

  3. 区块链是什么?区块链能做什么?区块链学习路线分享

    2017年区块链在某些领域的应用已经相当成熟,比特币(加密数字货币),以太坊(区块链开发平台),超级账本(面向企业的分布式账本平台).也有一些处在起步阶段的应用(智能合约,证券.资产管理,公证防伪,知 ...

  4. 怎么样才能更高效的学习区块链

    一.为什么选择区块链 选择区块链作为实践学习的案例,原因有三: 第一,区块链是我最近两三个月刚学习的领域,对我来说也是一门从零开始学习的领域,这样的学习案例最具有指导作用.因为时间没有隔太久,很多学习 ...

  5. 区块链学习——区块链的技术栈

    摘要 我在区块链学习的上一篇博文,链接:区块链学习--区块链技术理念与工作流程中,简单介绍了区块链的技术理念以及工作流程,本文我将继续介绍区块链技术栈. 我们知道,区块链本身只是一个数据的记录格式,就 ...

  6. 如何学习区块链技术?

    2018年春节最火热的概念应该就是区块链了,从百度的莱茨狗和网易星球刷屏朋友圈,到3点钟区块链无眠群的大火,大佬们纷纷进军区块链,不了解区块链好像错过一个时代. 这里把学习区块链过程中的一些资料进行索 ...

  7. 如何系统学习区块链技术-干货来袭

    区块链术涉及面很广,很多开发人员看了一些资料后,感觉似懂非懂. 如何系统的学习区块链技术,是很多想从事区块链开发的程序员的疑问,本文内容有点多,一次吸收不完可以收藏以后再用. 苦口婆心写在前面: 从事 ...

  8. 要凉?46%开发者表示短期内不考虑学习区块链技术,拿什么拯救你我的区块链人才荒...

    程序员应该选择什么技术领域才能获得最高的回报? 本文详细解读了 2018 年最热门的五大领域,对行业现状.薪资概况及具体的技能要求给出了深入的分析,希望给担心"入错行"的你提供些指 ...

  9. 【联邦学习+区块链】FLchain: Federated Learning via MEC-enabled Blockchain Network

    文章目录 1. Introduction 2. Preliminaries and Definition 3. System Model 4. Blockchain operations in FLc ...

  10. 该怎么学习区块链技术?

    1. 入圈之前 出来学习的第一步是出来.在区块链的世界里面,链上的数据都是资产,所以骗子多,入圈的第一步当然是要保护好自己.建议在正式入圈前,先完整的阅读这个文档,这是安全大牛余弦出品,必属精品.看完 ...

最新文章

  1. 74. Leetcode 501. 二叉搜索树中的众数 (二叉搜索树-中序遍历类)
  2. AI:2020年6月21日北京智源大会演讲分享之09:20-09:40黄铁军教授《智源进展报告》
  3. 另一个程序正在使用此文件 进程无法访问 iis
  4. java 公共组件_【JAVA语言程序设计基础篇】--Swing GUI组件的公共特性
  5. maven error in opening zip file报错解决
  6. LeetCode 1864. 构成交替字符串需要的最小交换次数
  7. HDFS查看异常:Operation category READ is not supported in state standby. Visit
  8. 不定长内存池之apr_pool
  9. 鉴赏交流在海报设计中的作用
  10. java udp 流量控制_基于UDP传输协议的实现分析之流量和拥塞控制
  11. docker(二)基本操作
  12. 万年历插件软件测试,一个计算万年历的简单程序
  13. Flex 学习随笔 --- 找学习资料+安装环境+工具
  14. 电商运营学习成长目录
  15. SAP《MM学习指南》操作记录----仓库盘点
  16. 二维码内置图片,并且把二维码放在图片指定位置上,画字,设置字本地样式,二维码批量生成
  17. 本地搭建wooyun图片无法加载问题解决
  18. 通过瑞利判据对显微镜物镜进行分辨率研究
  19. 上层应用程序是如何访问到底层驱动程序的呢?
  20. maya 中切换当前渲染器的方法和设置

热门文章

  1. JavaSE基础篇之-Java 流(Stream)、文件(File)和IO
  2. mybatis注解的使用
  3. maven中,xml文件无法编译,想要在Java中写xml文件,需要配置xml信息,另外springBoot设置如何在资源目录下扫描xml文件
  4. logback.xml日志配置文件,springboot
  5. ue4缓存位置怎么改_[UE4]动态液体材质浅谈
  6. [转]nodejs Error: request entity too large解决方案
  7. 写高性能 Web 应用程序的 10 个技巧 转自微软资料 .
  8. 01-信贷路由项目架构和 rose 框架的搭建
  9. IAT 注入ImportInject(dll)
  10. 解决windows 下 mysql命令行导入备份文件 查询时乱码的问题