区块链是什么,比特币是什么,各种虚拟货币是什么,我这里一概不做解答。
区块链本身是一种技术。
利用区块链这个概念炒币的都是心怀鬼胎的。
我们回归技术本身,这里结合我自己对区块链的理解,还有参考别人的文章,用Python简易构建一个区块链。
Now, begin!
一. 区块结构,交易信息,链构成,区块hash,工作量证明pow算法等几个概念
1.1 区块结构由区块索引,Unix时间戳,交易信息,工作量证明,上一个区块的hash组成,交易信息包含发送者信息,接收者信息和交易数量。其中区块中的previous_hash表示上一个区块整体的hash值,这又是说,所有的区块中(除去创世块)都包含有上一个区块的hash值(有点类似斐波那契数列,每一个值都跟前一个值有对应的关系),这样一一链接在一起形成有序,规整,不可逆的链式结构,这就是所谓的区块链。
block = { 'index' : index, # 区块索引 'timestamp' : time(),# unix时间戳 'transactions': { # 交易信息 'sender': sender, # 发送者 'recipient': recipient, # 接收者 'amount': amount # 数量 }), 'proof' : proof, # 工作量证明 'previous_hash' : 'xxxxx' # 上一个区块的hash }
1.2 区块的hash
比特币采用的加密算法是sha256,这个算法的破解难度及其大,自行了解。我们这里也采用sha256进行区块的hash加密。
def hash(block): """生成区块的SHA-256 hash值""" block_string = json.dumps(block, sort_keys=True).encode() return hashlib.sha256(block_string).hexdigest()
1.3 理解工作量证明(proof of work)
新区块的产生都需要经过工作量证明,俗称“挖矿”。
新的区块以来工作量证明算法(poW)来构造,poW的目标是找出一个符合特定的数字,这个数字很难计算出来,但容易验证,这就是工作量证明的核心思想。而且这个工作量证明算法也是当前区块跟上一个区块相关联的,我们这里这样简易设计了一个pow算法。
pow:查找一个P'使得hash(pp')以4个0开头,其中p是上一个区块的证明,p'是当前的证明。
我们这里定义两个方法去模拟实现这个“挖矿”过程
# 简易实现pow算法 def proof_of_work(self, last_proof): """ 简单的工作量证明 查找一个P'使得hash(pp')以4个0开头 p是上一个区块的证明,p'是当前的证明 """ proof = 0 while self.valid_proof(last_proof, proof) is False: proof += 1 return proof # 验证工作证明是否正确 def valid_proof(last_proof, proof): """验证证明:是否hash(last_proof,proof)以4个0 开头""" guess = f'{last_proof}{proof}'.encode() guess_hash = hashlib.sha256(guess).hexdigest() return guess_hash[:4] == '0000'
二. 区块产生
2.1 节点注册,挖矿得先有矿工啊
我们提供一个节点注册的方法,这里比较简单,用网络地址代表一个矿工节点。
register_node(, address): parsed_url = urlparse(address) .nodes.add(parsed_url.netloc)
2.2 新交易信息生成,新区块生成
new_transaction方法负责添加一个新的交易信息
new_block负责把最新的一个交易信息加入到区块链中
# 生成新区块 def new_block(self, proof, previous_hash=None): """生成新块""" block = { 'index': len(self.chain) + 1, 'timestamp': time(), 'transactions': self.current_transactions, 'proof': proof, 'previous_hash': previous_hash or self.hash(self.chain[-1]) } # 重置当前交易区块 self.current_transactions = [] # 添加新区块 self.chain.append(block) return block # 生成新交易信息,信息将加入到下一个待挖的区块中 def new_transaction(self, sender, recipient, amount): self.current_transactions.append({ 'sender': sender, 'recipient': recipient, 'amount': amount }) return self.last_block['index'] + 1
三. 矿工
矿工这边实际上相当于一个节点(客户端),我们是用Flask框架模拟这个节点。
新建一个py文件写客户端(节点)代码,上面的都是区块链服务端的代码。
客户端代码,我们写成api形式,只返回json数据。
3.1 节点注册
# 注册节点 @app.route('/nodes/register', methods=['POST']) def register_nodes(): address = request.values.get('address') if address is None: return '缺失参数', 400 blockchain.register_node(address) response = { 'message': '新的节点已经被添加', 'total_nodes': list(blockchain.nodes) } return jsonify(response), 201
3.2 去挖矿
前面我们说到,每个区块都需要经过工作量证明,客户端挖矿就是不断的去找一个的数使pow算法给定的条件成立,直至条件成立。根据pow算法的难易度,让计算机去执行不同计算量的的计算,这也就是为什么这个动作为什么被称为“挖矿了”。所以说,配置更高,性能越好的计算机拥有更快的运算力量,在与其他矿工的竞争中就更具优势。
这样的挖矿动作消耗了全球很大的计算机的运力和电力!
我们这里发起一个请求,然后去调用pow算法,直至解出正确答案。
如果解出正确答案,就相当于挖出一个新的区块(既是挖矿者获得一个),并添加交易,然后添加到区块链中。
# 去挖掘新的区块 @app.route('/mine', methods=['GET']) def mine(): last_block = blockchain.last_block # 已存在的最新的区块 last_proof = last_block['proof'] # 模拟挖矿动作,如果挖掘到新的区块并返回新区块的工作量证明 proof = blockchain.proof_of_work(last_proof) # 当前节点挖到新的区块,生成一个新的交易信息 blockchain.new_transaction( sender='0', # 发送者为0表明是新挖出的区块 recipient=node_identifier, amount=1 ) # 把当前节点挖到的区块加入到区块链中 block = blockchain.new_block(proof, None) # 返回响应数据 response = { 'message': '新的区块已经创建', 'index': block['index'], 'transactions': block['transactions'], 'proof': block['proof'], 'previous_hash': block['previous_hash'] } return jsonify(response), 200
3.3 解决冲突,实现共识
我们这里模拟了一个挖矿节点,实际中可能存在很多个节点,每个节点都保存有一个链,如何去保证这个链的一致性呢?
这里我们规定,网络中所有的链中的最长的,有效的链为实际的链。
有效是指每个区块都是正确的,没有经过任何篡改的,所以需要校验每个区块的hash和上一个区块的hash,并且还要校验每个区块的工作量证明正确与否。
最长的就是指最长的链。
定义两个方法去解决冲突,实现共识:
# 简易实现共识算法 def resolve_conflicts(self): """ 冲突是指不同的节点拥有不同的链,为了解决这个问题,规定最长的, 有效的链才是最终的链。网络中有效最长链才是实际的链。 """ neighbours = self.nodes # 网络中所有的节点 new_chain = None max_length = len(self.chain) # 遍历所有的节点 for node in neighbours: response = requests.get(f'http://{node}/chain') if response.status_code == 200: length = response.json()['length'] chain = response.json()['chain'] if length > max_length and self.valid_chain(chain): max_length = length new_chain = chain # 如果发现有效更长链,就替换掉自己的链 if new_chain: self.chain = new_chain return True return False # 验证区块链是否是有效区块链 def valid_chain(self, chain): """遍历每个块,验证hash和proof""" last_block = chain[0] # 第一个区块 current_index = 1 while current_index < len(chain): block = chain[current_index] # 1.验证当前区块的previous_hash是否和上一个区块的hash相等 if block['previous_hash'] != self.hash(last_block): return False # 2.验证工作量证明 if not self.valid_proof(last_block['proof'], block['proof']): return False last_block = block current_index += 1 return True
然后在我们的客户端代码中,去发起一个请求解决冲突,就是比对网络上所有节点的链,如果找到有效更长链,就将这个有效更长链替换掉自己节点原来的链。
# 解决冲突 @app.route('/nodes/resolve', methods=['GET']) def consensus(): replaced = blockchain.resolve_conflicts() if replaced: response = { 'message': '发现有效更长链,我们自己的链已被替换为该链', 'new_chain': blockchain.chain, } else: response = { 'message': '我们的链是有效更长链', 'chain': blockchain.chain } return jsonify(response), 200
四. 进行挖矿
运行flask应用,
访问127.0.0./mine模拟挖矿动作,访问3次后,此时应该有4个块(包含一个创世块)
定义应该方法去查看全部区块
还可以去发起一个交易,这里就不赘述了。
总结:区块链作为一个技术而言,还是有研究的价值的。对于其应用场景,见仁见智,我不说。另外,这个代码全在托管在gitee上的blockchain上。
我个人水平受限,可能有些地方理解也不是很透彻,如有错误,希望指正。
还有就是可以访问我的项目查看我最近写的代码。