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

区块链中的共识算法

在比特币公链架构解析中,就曾提到过为了实现去中介化的设计,比特币设计了一套共识协议,并通过此协议来保证系统的稳定性和防攻击性。 并且我们知道,截止目前使用最广泛,也是最被大家接受的共识算法,是我们先前介绍过的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 sha256
from datetime import datetime
def 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 newblock
def 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, ThreadingTCPServer
def 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 threading
from 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算法的区块链,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 使用Python从零开始撸一个区块链

    作者认为最快的学习区块链的方式是自己创建一个,本文就跟随作者用Python来创建一个区块链. 对数字货币的崛起感到新奇的我们,并且想知道其背后的技术--区块链是怎样实现的. 但是完全搞懂区块链并非易事,我喜欢在实践中学习,通过写代码来学习技术会掌握得更牢固.通过构建一个区块链可以加深对区块链的理解. 准备工作 本文要求读者对Python有基本的理解,能读写基本的Python,并且需要对HTTP请求有基本的了解. 我们知道区块链是由区块的记录构成的不可变.有序的链结构,记录可以是交易.文件或任何你

  • python简单区块链模拟详解

    最近学习了一点python,那就试着做一做简单的编程练习. 首先是这个编程的指导图,如下: 对的,类似一个简单区块链的模拟. 代码如下: class DaDaBlockCoin: #index 索引,timestamp 时间戳,data 交易记录,self_hash交易hash,last_hash,上个hash def __init__(self,idex,timestamp,data,last_hash): self.idex = idex self.timestamp = timestamp

  • 用不到50行的Python代码构建最小的区块链

    译者注:随着比特币的不断发展,它的底层技术区块链也逐步走进公众视野,引起大众注意.本文用不到50行的Python代码构建最小的数据区块链,简单介绍了区块链去中心化的结构与其实现原理. 尽管一些人认为区块链是一个等待问题的解决方案,但毫无疑问,这种新技术是计算机的奇迹.但是,区块链到底是什么呢? 区块链 它是比特币或其他加密货币进行交易的数字账本,账本按时间顺序记录并对外公开. 在更一般的术语中,它是一个公共数据库,新数据存储在一个名为块的容器中,并被添加到一个不可变链(后来的区块链)中添加了过去

  • Python从零开始创建区块链

    作者认为最快的学习区块链的方式是自己创建一个,本文就跟随作者用Python来创建一个区块链. 对数字货币的崛起感到新奇的我们,并且想知道其背后的技术--区块链是怎样实现的. 但是完全搞懂区块链并非易事,我喜欢在实践中学习,通过写代码来学习技术会掌握得更牢固.通过构建一个区块链可以加深对区块链的理解. 准备工作 本文要求读者对Python有基本的理解,能读写基本的Python,并且需要对HTTP请求有基本的了解. 我们知道区块链是由区块的记录构成的不可变.有序的链结构,记录可以是交易.文件或任何你

  • Python实现类似比特币的加密货币区块链的创建与交易实例

    虽然有些人认为区块链是一个早晚会出现问题的解决方案,但是毫无疑问,这个创新技术是一个计算机技术上的奇迹.那么,究竟什么是区块链呢? 区块链 以比特币(Bitcoin)或其它加密货币按时间顺序公开地记录交易的数字账本. 更通俗的说,它是一个公开的数据库,新的数据存储在被称之为区块(block)的容器中,并被添加到一个不可变的链(chain)中(因此被称为区块链(blockchain)),之前添加的数据也在该链中.对于比特币或其它加密货币来说,这些数据就是一组组交易,不过,也可以是其它任何类型的数据

  • Python学习入门之区块链详解

    前言 本文将给大家简单介绍关于区块链(BlockChain)的相关知识,并用Python做一简单实现.下面话不多说,来一起看看详细的介绍: 什么是区块链 简单来说,区块链就是把加密数据(区块)按照时间顺序进行叠加(链)生成的永久.不可逆向修改的记录.具体来说,它区块链是由一串使用密码学方法产生的数据块组成的,每一个区块都包含了上一个区块的哈希值(hash),从创始区块(genesis block)开始连接到当前区块,形成块链.每一个区块都确保按照时间顺序在上一个区块之后产生,否则前一个区块的哈希

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

    区块链中的共识算法 在比特币公链架构解析中,就曾提到过为了实现去中介化的设计,比特币设计了一套共识协议,并通过此协议来保证系统的稳定性和防攻击性. 并且我们知道,截止目前使用最广泛,也是最被大家接受的共识算法,是我们先前介绍过的POW(proof of work)工作量证明算法.目前市值排名前二的比特币和以太坊也是采用的此算法. 虽然POW共识算法取得了巨大的成功,但对它的质疑也从来未曾停止过. 其中最主要的一个原因就是电力消耗.据不完全统计,基于POW的挖矿机制所消耗的电量是非常巨大的,甚至比

  • Python实现基于KNN算法的笔迹识别功能详解

    本文实例讲述了Python实现基于KNN算法的笔迹识别功能.分享给大家供大家参考,具体如下: 需要用到: Numpy库 Pandas库 手写识别数据 点击此处本站下载. 数据说明: 数据共有785列,第一列为label,剩下的784列数据存储的是灰度图像(0~255)的像素值 28*28=784 KNN(K近邻算法): 从训练集中找到和新数据最接近的K条记录,根据他们的主要分类来决定新数据的类型. 这里的主要分类,可以有不同的判别依据,比如"最多","最近邻",或者

  • 从0编写区块链之用python解释区块链最基本原理

    人工智能和区块链诞生至今已经有了十几年,当这些技术出现时,人们都说他们会改变世界,但至今为止,这两项技术对现实的影响依然有限.从技术上看人工智能的原理其实是从大量数据中寻找规律或模式,但区块链的技术原理是什么呢?在我看来区块链的原理一直处于云里雾里,有很多近乎玄学的解释将其笼罩,有人从经济学解释,有人从社会学解释,从”人文“角度解释的区块链总是过于夸大其词,这些说法中往往又包含不良用心. 由此我想去芜存真,我们不用关心区块链如何”改变世界“,我们就从纯技术角度去探讨,其实区块链和人工智能一样,从

  • python区块链简易版交易完善挖矿奖励示例

    目录 说明 引言 奖励 UTXO 集 Merkle 树 P2PKH 总结 说明 本文根据https://github.com/liuchengxu/blockchain-tutorial的内容,用python实现的,但根据个人的理解进行了一些修改,大量引用了原文的内容.文章末尾有"本节完整源码实现地址". 引言 在这个系列文章的一开始,我们就提到了,区块链是一个分布式数据库.不过在之前的文章中,我们选择性地跳过了“分布式”这个部分,而是将注意力都放到了“数据库”部分.到目前为止,我们几

  • python区块链地址的简版实现

    说明 本文根据https://github.com/liuchengxu/blockchain-tutorial 的内容,用python实现的,但根据个人的理解进行了一些修改,大量引用了原文的内容.文章末尾有"本节完整源码实现地址". 引言 在上一篇文章中,我们已经初步实现了交易.相信你应该了解了交易中的一些天然属性,这些属性没有丝毫“个人”色彩的存在:在比特币中,没有用户账户,不需要也不会在任何地方存储个人数据(比如姓名,护照号码或者 SSN).但是,我们总要有某种途径识别出你是交易

  • Python实现基于二叉树存储结构的堆排序算法示例

    本文实例讲述了Python实现基于二叉树存储结构的堆排序算法.分享给大家供大家参考,具体如下: 既然用Python实现了二叉树,当然要写点东西练练手. 网络上堆排序的教程很多,但是却几乎都是以数组存储的数,直接以下标访问元素,当然这样是完全没有问题的,实现简单,访问速度快,也容易理解. 但是以练手的角度来看,我还是写了一个二叉树存储结构的堆排序 其中最难的问题就是交换二叉树中两个节点. 因为一个节点最多与三个节点相连,那么两个节点互换,就需要考虑到5个节点之间的关系,也需要判断是左右孩子,这将是

随机推荐