天涯论坛

 找回密码
 立即注册
搜索
查看: 59|回复: 5

Python创建区块链,邀请伴侣一块来测试!

[复制链接]

3047

主题

3万

回帖

9606万

积分

论坛元老

Rank: 8Rank: 8

积分
96065852
发表于 2024-7-29 09:51:09 | 显示全部楼层 |阅读模式

对数字货币的崛起感到新奇的咱们,并且想晓得背面的技术——区块链是怎么样实现的。作者认为最快的学习区块链的方式是自己创建一个,本文就跟随作者用Python来创建一个区块链。在实践中学习,经过构建一个区块链能够加深对区块链的理解。

准备工作

本文需求读者对Python有基本的理解,能读写基本的Python,并且需要对HTTP请求有基本的认识。点击查看Python难懂?买一次西瓜就懂了!HTTP请求:http://www.runoob.com/http/http-tutorial.html(复制到浏览器查看)

咱们晓得区块链是由于区块的记录形成的不可变、有序的链结构,记录能够是交易、文件或任何你想要的数据,重要的是它们是经过哈希值(hashes)链接起来的。

环境准备

保证已然安装Python3.6+, pip , Flask, requests,安装办法

pip install Flask==0.12.2 requests==2.18.4

同期还需要一个HTTP客户端,例如Postman,cURL或其它客户端。参考

https://github.com/xilibi2003/blockchain

起始创建Blockchain

新建一个文件 blockchain.py,本文所有的代码都写在这一个文件中。

Blockchain类

首要创建一个Blockchain类,在构造函数中创建了两个列表,一个用于储存区块链,一个用于储存交易。以下是Blockchain类的框架:

class Blockchain(object):   def __init__(self)

:

       self

.chain = []

       self

.current_transactions = []

   def new_block(self)

:

       # Creates a new Block and adds it to the chain

       pass

   def new_transaction(self)

:

       # Adds a new transaction to the list of transactions

       pass

   @staticmethod

   def hash(block)

:

       # Hashes a Block

       pass

   @property

   def last_block(self)

:

       # Returns the last Block in the chain       pass

向右移动,查看完整文本

Blockchain类用来管理链条,它能存储交易,加入新块等,下面咱们来进一步完善这些办法

块结构

每一个区块包含属性:索引(index),Unix时间戳(timestamp),交易列表(transactions),工作量证明(稍后解释)以及前一个区块的Hash值。以下是一个区块的结构:

block = {

   index1

,

   timestamp1506057125.900785

,

   transactions

: [

       {

           sender"8527147fe1f5426f9dd545de4b27ee00"

,

           recipient"a77f5cdfa2934df3954a5c7c7da5df1f"

,

           amount5

,

       }

   ],

   proof324984774000

,

   previous_hash"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"}

向右移动,查看完整文本

这儿,区块链的概念就清楚了,每一个新的区块都包括上一个区块的Hash,这是关键的一点,它保证了区块链不可变性。倘若攻击者破坏了前面的某个区块,那样后面所有区块的Hash都会变得不正确。不睬解的话,慢慢消化。

加入交易

接下来咱们需要添加一个交易,来完善下new_transaction办法

class Blockchain(object):

   ...

   def new_transaction(self, sender, recipient, amount):       

"""

生成新交易信息,信息将加入到下一个待挖的区块中

       :param sender: Address of the Sender

       :param recipient: Address of the Recipient

       :param amount: Amount

       :return: The index of the Block that will hold this transaction

        """

       self.current_transactions.append({

           sender

: sender,

           recipient

: recipient,

           amount

: amount,

       })

       return self.last_block[index] + 1

向右移动,查看完整文本

方法向列表中添加一个交易记录,并返回该记录将被添加到的区块(下一个待挖掘的区块)的索引,等下在用户提交交易时会有用。

创建新块

当Blockchain实例化后,咱们需要构造一个创世块(前区块的第1个区块),并且给它加上一个工作量证明。每一个区块都需要经过工作量证明,俗叫作挖矿,稍后会继续讲解。为了构造创世块,咱们还需要完善new_block(), new_transaction() 和hash() 办法

import

 hashlib

import

 json

from time import

 time

class Blockchain(object):   def __init__(self):

self.current_transactions = []

       self.chain = []

       # Create the genesis block       self.new_block(previous_hash=1, proof=100

)

   def new_block(self, proof, previous_hash=None):       

"""

生成新块

       :param proof: The proof given by the Proof of Work algorithm

        :param previous_hash: (Optional) Hash of previous Block

       :return: New Block

       """

       block = {

           index: len(self.chain) +1

,

           timestamp

: time(),

           transactions

: self.current_transactions,

           proof

: proof,

           previous_hash: previous_hash or self.hash(self.chain[-1

]),

       }

       # Reset the current list of transactions

       self.current_transactions = []

       self.chain.append(block)

       return

 block

   def new_transaction(self, sender, recipient, amount):       

"""

       生成新交易信息,信息将加入到下一个待挖的区块中

       :param sender: Address of the Sender

       :param recipient: Address of the Recipient

       :param amount: Amount

       :return: The index of the Block that will hold this transaction

        """

       self.current_transactions.append({

           sender

: sender,

           recipient

: recipient,

           amount

: amount,

       })

       return self.last_block[index] + 1   @property   def last_block(self):       return self.chain[-1

]

   @staticmethod   def hash(block):       

"""

       生成块的 SHA-256 hash值

       :param block: Block

       :return:

       """
       # We must make sure that the Dictionary is Ordered, or well have inconsistent hashes        block_string = json.dumps(block, sort_keys=True

).encode()

       returnhashlib.sha256(block_string).hexdigest()

向右移动,查看完整文本

经过上面的代码和注释能够对区块链有直观的认识,接下来咱们瞧瞧区块是怎么挖出来的。

理解工作量证明

新的区块依赖工作量证明算法(PoW)来构造,PoW的目的是找出一个符合特定要求的数字,这个数字很难计算出来,但容易验证。这便是工作量证明的核心思想。

为了方便理解,举个例子:假设一个整数 x 乘以另一个整数 y 的积的 Hash 值必须以 0 结尾,即hash(x * y) = ac23dc…0,设变量 x = 5,求 y 的值?用Python实现如下:

from hashlib import

 sha256

x = 5y = 0  # y未知whilesha256(f{x*y}.encode()).hexdigest()[-1] != "0"

:

   y += 1print(fThe solution is y = {y})

向右移动,查看完整文本

结果是y=21. 由于

hash(5 * 21) = 1253e9373e...5e3600155e860

在比特币中,运用叫作为Hashcash的工作量证明算法,它和上面的问题很类似。矿工们为了争夺创建区块的权利而争相计算结果。一般,计算难度与目的字符串需要满足的特定字符的数量成正比,矿工算出结果后,会得到比特币奖励。

当然,在网络上非常容易验证这个结果。

实现工作量证明

咱们来实现一个类似PoW算法,规则是:寻找一个数 p,使得它与前一个区块的 proof 拼接成的字符串的 Hash 值以 4 个零开头。

import

 hashlib

import

 json

from time import

 time

from uuid import

 uuid4

class Blockchain(object):

   ...

   def proof_of_work(self, last_proof):       

"""

       简单的工作量证明:

        - 查询一个 p 使得 hash(pp) 以4个0开头

        - p 是上一个块的证明,  p 是当前的证明

       :param last_proof:

       :return:

       """
       proof = 0       whileself.valid_proof(last_proof, proof)is False

:

           proof += 1       return

 proof

   @staticmethod   def valid_proof(last_proof, proof):       

"""

       验证证明: 是不是hash(last_proof, proof)以4个0开头?

       :param last_proof: Previous Proof

       :param proof: Current Proof

       :return: True if correct, False if not.

       """
       guess = f{last_proof}{proof}

.encode()

guess_hash = hashlib.sha256(guess).hexdigest()

       return guess_hash[:4] == "0000"

向右移动,查看完整文本

衡量算法繁杂度的办法是修改零开头的个数。运用4个来用于演示,你会发掘多一个零都会大大增多计算出结果所需的时间。此刻Blockchain类基本已然成为了,接下来运用HTTP requests来进行交互。

Blockchain做为API接口

咱们运用Python Flask框架,这是一个轻量Web应用框架,它方便将网络请求映射到 Python函数,此刻咱们来让Blockchain运行在基于Flask web上。

咱们将创建三个接口:

/transactions/new 创建一个交易并添加到区块

/mine 告诉服务器去挖掘新的区块

/chain 返回全部区块链

创建节点

咱们的Flask服务器将扮演区块链网络中的一个节点。咱们先添加有些框架代码:

import

 hashlib

import

 json

from textwrap import

 dedent

from time import

 time

from uuid import

 uuid4

from flask import

 Flask

class Blockchain(object):

   ...

# Instantiate our Node

app = Flask(__name__)

# Generate a globally unique address for this nodenode_identifier = str(uuid4()).replace(-

, )

# Instantiate the Blockchain

blockchain = Blockchain()

@app.route(/mine, methods=[GET])def mine():   return "Well mine a new Block"@app.route(/transactions/new, methods=[POST])def new_transaction():   return "Well add a new transaction"@app.route(/chain, methods=[GET])def full_chain():

   response = {

       chain

: blockchain.chain,

       length

: len(blockchain.chain),

   }

   return jsonify(response), 200if __name__ == __main__

:

   app.run(host=0.0.0.0, port=5000

)

向右移动,查看完整文本

简单的说明一下以上代码:

第15行: 创建一个节点.

第18行: 为节点创建一个随机的名字.

第21行: 实例Blockchain类.

第24–26行: 创建/mine GET接口。

第28–30行: 创建/transactions/new POST接口,能够给接口发送交易数据.

第32–38行: 创建 /chain 接口, 返回全部区块链.

第40–41行: 服务运行在端口5000上.

发送交易

发送到节点的交易数据结构如下:

{

"sender""my address"

,

"recipient""someone elses address"

,

"amount"5}

之前已然有添加交易的方法,基于接口来添加交易就很简单了

import

 hashlib

import

 json

from textwrap import

 dedent

from time import

 time

from uuid import

 uuid4

from flask import

 Flask, jsonify, request

...

@app.route(/transactions/new, methods=[POST])def new_transaction():

   values = request.get_json()

   # Check that the required fields are in the POSTed datarequired = [senderrecipientamount

]

   if not all(k in values for k in

 required):

       return Missing values400   # Create a new Transactionindex = blockchain.new_transaction(values[sender], values[recipient], values[amount

])

   response = {messagefTransaction will be added to Block {index}

}

   return jsonify(response), 201

向右移动,查看完整文本

挖矿

挖矿正是神奇所在,它很简单,做了一下三件事:

计算工作量证明PoW

经过新增一个交易授予矿工(自己)一个币

构造新区块并将其添加到链中

import

 hashlib

import

 json

from time import

 time

from uuid import

 uuid4

from flask import

Flask, jsonify, request

...

@app.route(/mine, methods=[GET])def mine():   # We run the proof of work algorithm to get the next proof...

last_block = blockchain.last_block

   last_proof = last_block[proof

]

   proof = blockchain.proof_of_work(last_proof)

   # 给工作量证明的节点供给奖励.   # 发送者为 "0" 显示是新挖出的币

   blockchain.new_transaction(

       sender="0"

,

recipient=node_identifier,

       amount=1

,

   )

   # Forge the new Block by adding it to the chain

   block = blockchain.new_block(proof)

   response = {

       message"New Block Forged"

,

       index: block[index

],

       transactions: block[transactions

],

       proof: block[proof

],

       previous_hash: block[previous_hash

],

   }

   return jsonify(response), 200

向右移动,查看完整文本

重视交易的接收者是咱们自己的服务器节点,咱们做的大部分工作都只是围绕Blockchain类办法进行交互。到此,咱们的区块链就算完成为了咱们实质运行下。

运行区块链

能够运用cURL 或Postman 去和API进行交互,起步server:

python

 blockchain.py

* Runingon http://127.0.0.1:5000/ (Press CTRL+C to quit)

向右移动,查看完整文本

咱们经过请求 http://localhost:5000/mine 来进行挖矿

经过post请求,添加一个新交易

倘若不是运用Postman,则用一下的cURL语句同样的:

curl -X POST -H "Content-Type: application/json" -d 

{

"sender": "d4ee26eee15148ee92c6cd394edd974e",

"recipient": "someone-other-address",

"amount": 5

}  "http://localhost:5000/transactions/new"

向右移动,查看完整文本

在挖了两次矿之后,就有3个块了,经过请求 http://localhost:5000/chain 能够得到所有的块信息。

{

 "chain"

: [

   {

     "index"1

,

     "previous_hash"1

,

     "proof"100

,

     "timestamp"1506280650.770839

,

     "transactions"

: []

   },

   {

     "index"2

,

     "previous_hash""c099bc...bfb7"

,

     "proof"35293

,

     "timestamp"1506280664.717925

,

     "transactions"

: [

       {

         "amount"1

,

         "recipient""8bbcb347e0634905b0cac7955bae152b"

,

         "sender""0"

       }

     ]

   },

   {

     "index"3

,

     "previous_hash""eff91a...10f2"

,

     "proof"35089

,

     "timestamp"1506280666.1086972

,

     "transactions"

: [

       {

         "amount"1

,

         "recipient""8bbcb347e0634905b0cac7955bae152b"

,

         "sender""0"

       }

     ]

   }

 ],

 "length"3}

向右移动,查看完整文本

一致性(共识)

咱们已然有了一个基本的区块链能够接受交易和挖矿。然则区块链系统应该是分布式的。既然是分布式的,那样咱们到底拿什么保准所有节点有一样的链呢?这便是一致性问题,咱们想要在网络上有多个节点,就必须实现一个一致性的算法。

注册节点

在实现一致性算法之前,咱们需要找到一种方式让一个节点晓得它相邻的节点。每一个节点都需要保留一份包括网络中其它节点的记录。因此呢咱们新增几个接口:

/nodes/register 接收URL形式的新节点列表

/nodes/resolve 执行一致性算法,处理任何冲突,保证节点持有正确的链

咱们修改下Blockchain的init函数并供给一个注册节点办法

...

from urllib.parse import

 urlparse

...

class Blockchain(object):   def __init__(self):

       ...

       self.nodes = set()

       ...

   def register_node(self, address):       

"""

Add a new node to the list of nodes

       :param address: Address of node. Eg. http://192.168.0.5:5000

        :return: None

       """

       parsed_url = urlparse(address)

       self.nodes.add(parsed_url.netloc)

向右移动,查看完整文本

咱们用 set 来储存节点,这是一种避免重复添加节点的简单办法

实现共识算法

前面说到,冲突指的是区别的节点持有区别的链,为认识决这个问题,规定最长的、有效的链才是最后的链,换句话说,网络中有效最长链才是实质的链。咱们运用一下的算法,来达到网络中的共识。

...

import requests

class Blockchain(object)

   ...

   def valid_chain(self, chain)

:

       ""

"

       Determine if a given blockchain is valid

       :param chain: A blockchain

       :return: True if valid, False if not

       "
""       last_block = chain[0

]

       current_index = 1       while

 current_index < len(chain):

           block = chain[current_index]

           print(f{last_block}

)

           print(f{block}

)

           print("\n-----------\n"

)

           # Check that the hash of the block is correct           if block[previous_hash] != self

.hash(last_block):

               return

 False

           # Check that the Proof of Work is correct           if not self.valid_proof(last_block[proof], block[proof

]):

               return

 False

           last_block = block

           current_index += 1       return

 True

   def resolve_conflicts(self)

:

       ""

"

共识算法解决冲突

       运用网络中最长的链.

       :return: True 倘若链被取代, 否则为False

       "
""       neighbours = self

.nodes

       new_chain = None

       # Were only looking for chains longer than ours       max_length = len(self

.chain)

       # Grab and verify the chains from all the nodes in our network       for node in neighbours:           response = requests.get(fhttp://{node}/chain

)

           ifresponse.status_code ==200

:

               length = response.json()[length

]

               chain = response.json()[chain

]

               # Check if the length is longer and the chain is valid               iflength > max_lengthand self

.valid_chain(chain):

                   max_length = length

                   new_chain = chain

       # Replace our chain if we discovered a new, valid chain longer than ours        if new_chain:           self

.chain = new_chain

           return

 True

       return False

向右移动,查看完整文本

第1办法 valid_chain() 用来检测是不是是有效链,遍历每一个块验证hash和proof;第二个办法 resolve_conflicts() 用来处理冲突,遍历所有的邻居节点,并用上一个办法检测链的有效性,倘若发掘有效更长链,就替换掉自己的链。

咱们添加两个路由,一个用来注册节点,一个用来处理冲突。

@app.route(/nodes/register, methods=[POST

])

def register_nodes()

:

   values = request.get_json()

nodes = values.get(nodes

)

   if nodes is None:       return "Error: Please supply a valid list of nodes"400   for node in nodes:

       blockchain.register_node(node)

   response = {

       messageNew nodes have been added

,

       total_nodes

: list(blockchain.nodes),

   }

   return jsonify(response), 201@app.route(/nodes/resolve, methods=[GET

])

def consensus()

:

   replaced = blockchain.resolve_conflicts()

   if replaced:

       response = {

           messageOur chain was replaced

,

           new_chain

: blockchain.chain

       }

   else:

       response = {

           message:Our chain is authoritative

,

           chain

: blockchain.chain

       }

   return jsonify(response), 200

向右移动,查看完整文本

能够区别设备运行节点,或在一台机机开启区别的网络端口来模拟多节点的网络,这儿在同一台设备开启区别的端口演示,在区别的终端运行一下命令,就起步了两个节点:http://localhost:5000 和 http://localhost:5001

pipenv

 run python blockchain.py

pipenv run python blockchain.py -p 5001

而后在节点2上挖两个块,保证是更长的链,而后在节点1上拜访接口/nodes/resolve ,此时节点1的链会经过共识算法被节点2的链取代。

好啦,你能够邀请伴侣一块来测试你的区块链!

End.

作者:Daniel van Flymen;译者:Tiny熊

源自:learnblockchain

原文链接:https://learnblockchain.cn/2017/10/27/build_blockchain_by_python/





上一篇:Dreamweaver cs6 网页设计教程
下一篇:黑客怎么样攻破一个网站?图文讲解全流程
回复

使用道具 举报

3089

主题

3万

回帖

9909万

积分

论坛元老

Rank: 8Rank: 8

积分
99098770
发表于 2024-9-26 08:53:09 | 显示全部楼层
你的见解独到,让我受益匪浅,非常感谢。
回复

使用道具 举报

3089

主题

3万

回帖

9909万

积分

论坛元老

Rank: 8Rank: 8

积分
99098770
发表于 2024-9-27 13:09:37 | 显示全部楼层
我完全同意你的观点,说得太对了。
回复

使用道具 举报

3070

主题

3万

回帖

9915万

积分

论坛元老

Rank: 8Rank: 8

积分
99158931
发表于 2024-10-1 09:19:56 | 显示全部楼层
外链发布论坛学习网络优化SEO。
回复

使用道具 举报

3048

主题

3万

回帖

9910万

积分

论坛元老

Rank: 8Rank: 8

积分
99109040
发表于 2024-10-8 16:52:00 | 显示全部楼层
可以发布外链的网站 http://www.fok120.com/
回复

使用道具 举报

3070

主题

3万

回帖

9915万

积分

论坛元老

Rank: 8Rank: 8

积分
99158931
发表于 2024-10-17 10:15:24 | 显示全部楼层
外链论坛的成功举办,是与各位领导、同仁们的关怀和支持分不开的。在此,我谨代表公司向关心和支持论坛的各界人士表示最衷心的感谢!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

站点统计|Archiver|手机版|小黑屋|天涯论坛 ( 非经营性网站 )|网站地图

GMT+8, 2024-11-23 09:36 , Processed in 0.144407 second(s), 21 queries .

Powered by Discuz! X3.4

Copyright © 2001-2023, Tencent Cloud.