侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Python实现基于POS算法的区块链

Python  /  管理员 发布于 7年前   160

区块链中的共识算法

在比特币公链架构解析中,就曾提到过为了实现去中介化的设计,比特币设计了一套共识协议,并通过此协议来保证系统的稳定性和防攻击性。 并且我们知道,截止目前使用最广泛,也是最被大家接受的共识算法,是我们先前介绍过的POW(proof of work)工作量证明算法。目前市值排名前二的比特币和以太坊也是采用的此算法。

虽然POW共识算法取得了巨大的成功,但对它的质疑也从来未曾停止过。 其中最主要的一个原因就是电力消耗。据不完全统计,基于POW的挖矿机制所消耗的电量是非常巨大的,甚至比绝大多数国家耗电量还要多。这对我们的资源造成了极大的浪费,此外随着比特大陆等公司的强势崛起,造成了算力的高度集中。

基于以上种种原因,更多的共识算法被提出来 POS、DPOS、BPFT等等。 今天我们就来认识POS(proof of stake)算法。

Proof of stake,译为权益证明。你可能已经猜到了,权益证明简单理解就是拥有更多token的人,有更大的概率获得记账权利,然后获得奖励。 这个概率具体有多大呢? 下面我们在代码实现中会展示,分析也放在后面。 当然,POS是会比POW更好吗? 会更去中心化吗? 现在看来未必,所以我们这里也不去对比谁优谁劣。 我们站在中立的角度,单纯的来讨论讨论POS这种算法。

代码实战

生成一个Block

既然要实现POS算法,那么就难免要生成一条链,链又是由一个个Block生成的,所以下面我们首先来看看如何生成Block,当然在前面的内容里面,关于如何生成Block,以及交易、UTXO等等都已经介绍过了。由于今天我们的核心是实现POS,所以关于Block的生成,我们就用最简单的实现方式,好让大家把目光聚焦在核心的内容上面。

我们用三个方法来实现生成一个合法的区块

  • calculate_hash 计算区块的hash值
  • is_block_valid 校验区块是否合法
  • generate_block 生成一个区块
from hashlib import sha256from datetime import datetimedef generate_block(oldblock, bpm, address): """ :param oldblock: :param bpm: :param address: :return: """ newblock = {  "Index": oldblock["Index"] + 1,  "BPM": bpm,  "Timestamp": str(datetime.now()),  "PrevHash": oldblock["Hash"],  "Validator": address } newblock["Hash"] = calculate_hash(newblock) return newblockdef calculate_hash(block): record = "".join([  str(block["Index"]),  str(block["BPM"]),  block["Timestamp"],  block["PrevHash"] ]) return sha256(record.encode()).hexdigest()def is_block_valid(newblock, oldblock): """ :param newblock: :param oldblock: :return: """ if oldblock["Index"] + 1 != newblock["Index"]:  return False if oldblock["Hash"] != newblock["PrevHash"]:  return False if calculate_hash(newblock) != newblock["Hash"]:  return False return True

这里为了更灵活,我们没有用类的实现方式,直接采用函数来实现了Block生成,相信很容易看懂。

创建一个TCP服务器

由于我们需要用权益证明算法来选择记账人,所以需要从很多Node(节点)中选择记账人,也就是需要一个server让节点链接上来,同时要同步信息给节点。因此需要一个TCP长链接。

from socketserver import BaseRequestHandler, ThreadingTCPServerdef run(): # start a tcp server serv = ThreadingTCPServer(('', 9090), HandleConn) serv.serve_forever()

在这里我们用了python内库socketserver来创建了一个TCPServer。 需要注意的是,这里我们是采用的多线程的创建方式,这样可以保证有多个客户端同时连接上来,而不至于被阻塞。当然,这里这个server也是存在问题的,那就是有多少个客户端连接,就会创建多少个线程,更好的方式是创建一个线程池。由于这里是测试,所以就采用更简单的方式了。

相信大家已经看到了,在我们创建TCPServer的时候,使用到了HandleConn,但是我们还没有定义,所以接下来我们就来定义一个HandleConn

消息处理器

下面我们来实现Handler函数,Handler函数在跟Client Node通信的时候,需要我们的Node实现下面的功能

  • Node可以输入balance(token数量) 也就是股权数目
  • Node需要能够接收广播,方便Server同步区块以及记账人信息
  • 添加自己到候选人名单 (候选人为持有token的人)
  • 输入BPM生成Block
  • 验证一个区块的合法性

感觉任务还是蛮多的,接下来我们看代码实现

import threadingfrom queue import Queue, Empty# 定义变量block_chain = []temp_blocks = []candidate_blocks = Queue() # 创建队列,用于线程间通信announcements = Queue()validators = {}My_Lock = threading.Lock()class HandleConn(BaseRequestHandler): def handle(self):  print("Got connection from", self.client_address)  # validator address  self.request.send(b"Enter token balance:")  balance = self.request.recv(8192)  try:   balance = int(balance)  except Exception as e:   print(e)  t = str(datetime.now())  address = sha256(t.encode()).hexdigest()  validators[address] = balance  print(validators)  while True:   announce_winner_t = threading.Thread(target=annouce_winner, args=(announcements, self.request,), daemon=True)   announce_winner_t.start()   self.request.send(b"\nEnter a new BPM:")   bpm = self.request.recv(8192)   try:    bpm = int(bpm)   except Exception as e:    print(e)    del validators[address]    break   # with My_Lock:   last_block = block_chain[-1]   new_block = generate_block(last_block, bpm, address)   if is_block_valid(new_block, last_block):    print("new block is valid!")    candidate_blocks.put(new_block)   self.request.send(b"\nEnter a new BPM:\n")   annouce_blockchain_t = threading.Thread(target=annouce_blockchain, args=(self.request,), daemon=True)   annouce_blockchain_t.start()

这段代码,可能对大多数同学来说是有难度的,在这里我们采用了多线程的方式,同时为了能够让消息在线程间通信,我们使用了队列。 这里使用队列,也是为了我们的系统可以更好的拓展,后面如果可能,这一节的程序很容易拓展为分布式系统。 将多线程里面处理的任务拆分出去成独立的服务,然后用消息队列进行通信,就是一个简单的分布式系统啦。(是不是很激动?)

由于这里有难度,所以代码还是讲一讲吧

 # validator address  self.request.send(b"Enter token balance:")  balance = self.request.recv(8192)  try:   balance = int(balance)  except Exception as e:   print(e)  t = str(datetime.now())  address = sha256(t.encode()).hexdigest()  validators[address] = balance  print(validators)

这一段就是我们提到的Node 客户端添加自己到候选人的代码,每链接一个客户端,就会添加一个候选人。 这里我们用添加的时间戳的hash来记录候选人。 当然也可以用其他的方式,比如我们代码里面的client_address

announce_winner_t = threading.Thread(target=annouce_winner, args=(announcements, self.request,),daemon=True)  announce_winner_t.start()def annouce_winner(announcements, request): """ :param announcements: :param request: :return: """ while True:  try:   msg = announcements.get(block=False)   request.send(msg.encode())   request.send(b'\n')  except Empty:   time.sleep(3)   continue

然后接下来我们起了一个线程去广播获得记账权的节点信息到所有节点。

self.request.send(b"\nEnter a new BPM:")   bpm = self.request.recv(8192)   try:    bpm = int(bpm)   except Exception as e:    print(e)    del validators[address]    break   # with My_Lock:   last_block = block_chain[-1]   new_block = generate_block(last_block, bpm, address)   if is_block_valid(new_block, last_block):    print("new block is valid!")    candidate_blocks.put(new_block)

根据节点输入的BPM值生成一个区块,并校验区块的有效性。 将有效的区块放到候选区块当中,等待记账人将区块添加到链上。

annouce_blockchain_t = threading.Thread(target=annouce_blockchain, args=(self.request,), daemon=True)  annouce_blockchain_t.start()def annouce_blockchain(request): """ :param request: :return: """ while True:  time.sleep(30)  with My_Lock:   output = json.dumps(block_chain)  try:   request.send(output.encode())   request.send(b'\n')  except OSError:   pass

最后起一个线程,同步区块链到所有节点。

看完了,节点跟Server交互的部分,接下来是最重要的部分,

POS算法实现

def pick_winner(announcements):  """  选择记账人  :param announcements:  :return:  """  time.sleep(10)  while True:    with My_Lock:      temp = temp_blocks    lottery_pool = [] #    if temp:      for block in temp:        if block["Validator"] not in lottery_pool:          set_validators = validators          k = set_validators.get(block["Validator"])          if k:for i in range(k):  lottery_pool.append(block["Validator"])      lottery_winner = choice(lottery_pool)      print(lottery_winner)      # add block of winner to blockchain and let all the other nodes known      for block in temp:        if block["Validator"] == lottery_winner:          with My_Lock:block_chain.append(block)          # write message in queue.          msg = "\n{0} 赢得了记账权利\n".format(lottery_winner)          announcements.put(msg)          break    with My_Lock:      temp_blocks.clear()

这里我们用pick_winner 来选择记账权利,我们根据token数量构造了一个列表。 一个人获得记账权利的概率为:

p = mount['NodeA']/mount['All']

文字描述就是其token数目在总数中的占比。 比如总数有100个,他有10个,那么其获得记账权的概率就是0.1, 到这里核心的部分就写的差不多了,接下来,我们来添加节点,开始测试吧

测试POS的记账方式

在测试之前,起始还有一部分工作要做,前面我们的run方法需要完善下,代码如下:

def run():  # create a genesis block  t = str(datetime.now())  genesis_block = {    "Index": 0,    "Timestamp": t,    "BPM": 0,    "PrevHash": "",    "Validator": ""  }  genesis_block["Hash"] = calculate_hash(genesis_block)  print(genesis_block)  block_chain.append(genesis_block)  thread_canditate = threading.Thread(target=candidate, args=(candidate_blocks,), daemon=True)  thread_pick = threading.Thread(target=pick_winner, args=(announcements,), daemon=True)  thread_canditate.start()  thread_pick.start()  # start a tcp server  serv = ThreadingTCPServer(('', 9090), HandleConn)  serv.serve_forever()def candidate(candidate_blocks):  """  :param candidate_blocks:  :return:  """  while True:    try:      candi = candidate_blocks.get(block=False)    except Empty:      time.sleep(5)      continue    temp_blocks.append(candi)if __name__ == '__main__':  run()

添加节点连接到TCPServer

为了充分减少程序的复杂性,tcp client我们这里就不实现了,可以放在后面拓展部分。 毕竟我们这个系统是很容易扩展的,后面我们拆分了多线程的部分,在实现tcp client就是一个完整的分布式系统了。

所以,我们这里用linux自带的命令 nc,不知道nc怎么用的同学可以google或者 man nc

启动服务 运行 python pos.py

打开3个终端

分别输入下面命令

nc localhost 9090

终端如果输出

Enter token balance:

说明你client已经链接服务器ok啦.

测试POS的记账方式

接下来依次按照提示操作。 balance可以按心情来操作,因为这里是测试,我们输入100,
紧接着会提示输入BPM,我们前面提到过,输入BPM是为了生成Block,那么就输入吧,随便输入个9. ok, 接下来就稍等片刻,等待记账。

输出如同所示

依次在不同的终端,根据提示输入数字,等待消息同步。

生成区块链

下面是我这边获得的3个block信息。

总结

在上面的代码中,我们实现了一个完整的基于POS算法记账的链,当然这里有许多值得扩展与改进的地方。

  • python中多线程开销比较大,可以改成协程的方式
  • TCP建立的长链接是基于TCPServer,是中心化的方式,可以改成P2P对等网络
  • 链的信息不够完整
  • 系统可以拓展成分布式,让其更健壮

大概列了以上几点,其他还有很多可以拓展的地方,感兴趣的朋友可以先玩玩, 后者等到我们后面的教程。 (广告打的措手不及,哈哈)

当然了,语言不是重点,所以在这里,我也实现了go语言的版本源码地址

go语言的实现感觉要更好理解一点,也显得要优雅一点。这也是为什么go语言在分布式领域要更抢手的原因之一吧!

项目地址

https://github.com/csunny/py-bitcoin/

参考

https://medium.com/@mycoralhealth/code-your-own-proof-of-stake-blockchain-in-go-610cd99aa658

总结

以上所述是小编给大家介绍的Python实现基于POS算法的区块链,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对站的支持!


  • 上一条:
    python实现彩票系统
    下一条:
    Python爬虫实现抓取京东店铺信息及下载图片功能示例
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客