区块链(Blockchain) 加入小组

9个成员 2个话题 创建时间:2018-01-18

边构建区块链边学习(Jupyter notebook) 精华

发表于01-18 520次查看
 

边构建区块链边学习(Jupyter notebook)

  • 构建区块链的同时进行学习|编程|使用在POSTD中发布的解释和代码逐步进行测试。

  • 直接由Jupyter实施,因为觉得在Flask中的实现很多余。

  • 阅读文本是假定您对于块链本身的机制已经有所理解。

 

代码的实现

1.创建一个区块链类

In [1]:
from time import time
import hashlib
import json

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):
        """
        Create a new Block in the Blockchain
        :param proof: <int> The proof given by the Proof of Work algorithm
        :param previous_hash: (Optional) <str> Hash of previous Block
        :return: <dict> 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):
        """
        Creates a new transaction to go into the next mined Block
        :param sender: <str> Address of the Sender
        :param recipient: <str> Address of the Recipient
        :param amount: <int> Amount
        :return: <int> 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

    @staticmethod
    def hash(block):
        """
        Creates a SHA-256 hash of a Block
        :param block: <dict> Block
        :return: <str>
        """
        # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()

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

    def proof_of_work(self, last_proof):
        """
        Simple Proof of Work Algorithm:
         - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
         - p is the previous proof, and p' is the new proof
        :param last_proof: <int>
        :return: <int>
        """
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1
        return proof

    @staticmethod
    def valid_proof(last_proof, proof):
        """
        Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
        :param last_proof: <int> Previous Proof
        :param proof: <int> Current Proof
        :return: <bool> True if correct, False if not.
        """
        guess = ('%d%d' % (last_proof, proof)).encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:4] == "0000"
 

2.挖矿代码

In [2]:
from uuid import uuid4

node_identifier = str(uuid4()).replace('-', '')

def mine(blockchain):
    global node_identifier
    
    # 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)

    # We must receive a reward for finding the proof.
    # The sender is "0" to signify that this node has mined a new coin.
    blockchain.new_transaction(
        sender="0",
        recipient=node_identifier,
        amount=1,
    )

    # Forge the new Block by adding it to the chain
    previous_hash = blockchain.hash(last_block)
    block = blockchain.new_block(proof, previous_hash)

    response = {
        'message': "New Block Forged",
        'index': block['index'],
        'transactions': block['transactions'],
        'proof': block['proof'],
        'previous_hash': block['previous_hash'],
    }
    return response
 

3.添加示例并返回整个链

In [3]:
def full_chain(blockchain):
    response = {
        'chain': blockchain.chain,
        'length': len(blockchain.chain),
    }
    return response
 

尝试移动块链

使用pprint使显示更容易看到。

In [4]:
import pprint
pp = pprint.PrettyPrinter(indent=2)
 

自身节点的标识符如下。

In [5]:
node_identifier
Out[5]:
'7d10057a10364156aa9ac7b92ce3c34e'
 

一旦实例化,将创建第一个区块

  • index:索引为1
  • previous_hash:初始的hash值1
  • length:链条的长度显然是1
In [6]:
b = Blockchain()
pp.pprint(full_chain(b))
 
{ 'chain': [ { 'index': 1,
               'previous_hash': 1,
               'proof': 100,
               'timestamp': 1516245610.8226993,
               'transactions': []}],
  'length': 1}
 

开始创建第一个街区

In [7]:
newblock = mine(b)
pp.pprint(newblock)
 
{ 'index': 2,
  'message': 'New Block Forged',
  'previous_hash': 'b0879b53a4230c49e23d3c4715034e732bc07ab54f3ba16dbd0fa73860d8faee',
  'proof': 35293,
  'transactions': [ { 'amount': 1,
                      'recipient': '7d10057a10364156aa9ac7b92ce3c34e',
                      'sender': '0'}]}
 

在交易环节中,只描述了采矿挖掘交易

  • sender:发件人为0
  • recipient:收件人标识符
  • amount:金额是1

记录为交易。 现在我们来看看整个区块链的内容

In [8]:
pp.pprint(full_chain(b))
 
{ 'chain': [ { 'index': 1,
               'previous_hash': 1,
               'proof': 100,
               'timestamp': 1516245610.8226993,
               'transactions': []},
             { 'index': 2,
               'previous_hash': 'b0879b53a4230c49e23d3c4715034e732bc07ab54f3ba16dbd0fa73860d8faee',
               'proof': 35293,
               'timestamp': 1516245625.9124067,
               'transactions': [ { 'amount': 1,
                                   'recipient': '7d10057a10364156aa9ac7b92ce3c34e',
                                   'sender': '0'}]}],
  'length': 2}
 

作为一个新的交易(交易),有的区块只包含挖掘结果。

我们在这里添加一个新的事务。

  • 添加发件人到'foo'
  • 将收件人设置为'bar'
  • amount 设置为10

并达成交易。

In [9]:
index = b.new_transaction('foo', 'bar', 10)
 

此时index(块的索引)是3。 上述交易存储在这个块中。

In [10]:
print(index)
 
3
 

此时,从整个链条来看,上面添加的交易还没有在链上注册。

In [11]:
pp.pprint(full_chain(b))
 
{ 'chain': [ { 'index': 1,
               'previous_hash': 1,
               'proof': 100,
               'timestamp': 1516245610.8226993,
               'transactions': []},
             { 'index': 2,
               'previous_hash': 'b0879b53a4230c49e23d3c4715034e732bc07ab54f3ba16dbd0fa73860d8faee',
               'proof': 35293,
               'timestamp': 1516245625.9124067,
               'transactions': [ { 'amount': 1,
                                   'recipient': '7d10057a10364156aa9ac7b92ce3c34e',
                                   'sender': '0'}]}],
  'length': 2}
 

挖矿并添加一个新的块。

In [12]:
newblock = mine(b)
 

创建第3个区块,保存之前创建的事务信息和挖掘信息。

In [13]:
pp.pprint(newblock)
 
{ 'index': 3,
  'message': 'New Block Forged',
  'previous_hash': '635b05a6a3d32c78f3d23fa9ab44222616ba073cac93f064fedeafb6684ad645',
  'proof': 35089,
  'transactions': [ {'amount': 10, 'recipient': 'bar', 'sender': 'foo'},
                    { 'amount': 1,
                      'recipient': '7d10057a10364156aa9ac7b92ce3c34e',
                      'sender': '0'}]}
 

此时整个链条的状态如下。

In [14]:
pp.pprint(full_chain(b))
 
{ 'chain': [ { 'index': 1,
               'previous_hash': 1,
               'proof': 100,
               'timestamp': 1516245610.8226993,
               'transactions': []},
             { 'index': 2,
               'previous_hash': 'b0879b53a4230c49e23d3c4715034e732bc07ab54f3ba16dbd0fa73860d8faee',
               'proof': 35293,
               'timestamp': 1516245625.9124067,
               'transactions': [ { 'amount': 1,
                                   'recipient': '7d10057a10364156aa9ac7b92ce3c34e',
                                   'sender': '0'}]},
             { 'index': 3,
               'previous_hash': '635b05a6a3d32c78f3d23fa9ab44222616ba073cac93f064fedeafb6684ad645',
               'proof': 35089,
               'timestamp': 1516245688.0261838,
               'transactions': [ { 'amount': 10,
                                   'recipient': 'bar',
                                   'sender': 'foo'},
                                 { 'amount': 1,
                                   'recipient': '7d10057a10364156aa9ac7b92ce3c34e',
                                   'sender': '0'}]}],
  'length': 3}
 

智能合约(分布式)

class Blockchain2()类的实现

实现Blcokchain 2类包含共识算法。

另外,节点标识符被保存为一个类成员,并且在挖掘它时被修改为使用它。 (为了能够处理多个节点的块链)

(实际上,定义Node类并将Blockchain 2类作为has-a作为成员似乎更好,但是因为在Blockchain类的原始版本中引入了register_node()或resolve_conflicts()

In [15]:
import copy

BlockchainNeighbours = {}

class Blockchain2(Blockchain):
    def __init__(self, node_identifier):
        super().__init__()
        self.nodes = set()
        self.node_identifier = node_identifier
        
    def register_node(self, node_identifier):
        """
        Add a new node to the list of nodes
        :node_identifier: <str> Node identifier of the neighbour node.
        :return: None
        """
        self.nodes.add(node_identifier)

    def valid_chain(self, chain):
        """
        Determine if a given blockchain is valid
        :param chain: <list> A blockchain
        :return: <bool> 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):
        """
        This is our Consensus Algorithm, it resolves conflicts
        by replacing our chain with the longest one in the network.
        :return: <bool> True if our chain was replaced, False if not
        """
        neighbours = self.nodes
        new_chain = None

        # We're 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:
            blockchain = BlockchainNeighbours[node]
            print('node id: %s, len: %d' % (blockchain.node_identifier, len(blockchain.chain)))

            # Check if the length is longer and the chain is valid
            if len(blockchain.chain) > max_length and self.valid_chain(blockchain.chain):
                max_length = len(blockchain.chain)
                new_chain = blockchain

        # Replace our chain if we discovered a new, valid chain longer than ours
        if new_chain:
            print("Replacing `%s' <- `%s'" % (self.node_identifier, new_chain.node_identifier))
            self.chain = copy.copy(new_chain.chain)
            return True

        return False
In [16]:
def mine2(blockchain):
    # 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)

    # We must receive a reward for finding the proof.
    # The sender is "0" to signify that this node has mined a new coin.
    blockchain.new_transaction(
        sender="0",
        recipient=blockchain.node_identifier,
        amount=1,
    )

    # Forge the new Block by adding it to the chain
    previous_hash = blockchain.hash(last_block)
    block = blockchain.new_block(proof, previous_hash)

    response = {
        'message': "New Block Forged",
        'index': block['index'],
        'transactions': block['transactions'],
        'proof': block['proof'],
        'previous_hash': block['previous_hash'],
    }
    return response
 

创建多个节点的块链

 

为三个节点创建一个块链,并注册为一个相邻节点。

In [35]:
# 为节点标识符为“foo”,“bar”,“buz”的三个节点创建一个块链。

foo = Blockchain2('foo')
bar = Blockchain2('bar')
buz = Blockchain2('buz')

# 注册在相邻节点的列表中
BlockchainNeighbours['foo'] = foo
BlockchainNeighbours['bar'] = bar
BlockchainNeighbours['buz'] = buz

# 'bar','buz'注册为'foo'节点邻居
foo.register_node('bar')
foo.register_node('buz')

# 为“bar”,“buz”节点注册邻居
bar.register_node('foo')
bar.register_node('buz')

buz.register_node('foo')
buz.register_node('bar')
 

在初始状态下,所有节点的链路长度为1。

In [18]:
print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))
 
foo: 1, bar: 1, buz: 1
 

即使你试图在初始状态下解决冲突,也没有链节点长于foo节点,所以foo节点链不会改变。

In [19]:
foo.resolve_conflicts()
 
node id: buz, len: 1
node id: bar, len: 1
Out[19]:
False
 

在一些节点上伸展块

接下来,在bar节点上挖掘并添加一个块。

In [20]:
pp.pprint(mine2(bar))
 
{ 'index': 2,
  'message': 'New Block Forged',
  'previous_hash': 'c97740db684709fd7455f413f0ad84f435236e1534caaea7cf744921b59fab3b',
  'proof': 35293,
  'transactions': [{'amount': 1, 'recipient': 'bar', 'sender': '0'}]}
 

节点的长度只有2

In [21]:
print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))
 
foo: 1, bar: 2, buz: 1
In [22]:
pp.pprint(foo.chain)
 
[ { 'index': 1,
    'previous_hash': 1,
    'proof': 100,
    'timestamp': 1516245713.7215648,
    'transactions': []}]
In [23]:
pp.pprint(bar.chain)
 
[ { 'index': 1,
    'previous_hash': 1,
    'proof': 100,
    'timestamp': 1516245713.7215648,
    'transactions': []},
  { 'index': 2,
    'previous_hash': 'c97740db684709fd7455f413f0ad84f435236e1534caaea7cf744921b59fab3b',
    'proof': 35293,
    'timestamp': 1516245772.022711,
    'transactions': [{'amount': 1, 'recipient': 'bar', 'sender': '0'}]}]
In [24]:
pp.pprint(buz.chain)
 
[ { 'index': 1,
    'previous_hash': 1,
    'proof': 100,
    'timestamp': 1516245713.7215648,
    'transactions': []}]
 

消除节点之间的冲突

在这种状态下,当试图解决foo节点处的冲突时,foo节点链被(更长的)节点链覆盖。

In [25]:
foo.resolve_conflicts()
 
node id: buz, len: 1
node id: bar, len: 2
Replacing `foo' <- `bar'
Out[25]:
True
 

当冲突的解决完成时,foo节点的链长变为2。

In [26]:
print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))
 
foo: 2, bar: 2, buz: 1
 

如果每个节点链的内容不同

 

接下来,考虑每个节点链的内容不同的情况。

这里我们看到foo节点和buz节点的内容不同的情况。

In [27]:
# buzノードの内容を揃える
buz.resolve_conflicts()
print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))
 
node id: foo, len: 2
node id: bar, len: 2
Replacing `buz' <- `foo'
foo: 2, bar: 2, buz: 2
 

在这里,在foo节点添加两个块,一个块在buz节点,并且添加具有不同事务的块。

In [28]:
foo.new_transaction('AAA', 'BBB', 123)
mine2(foo)
foo.new_transaction('CCC', 'DDD', 456)
mine2(foo)

buz.new_transaction('EEE', 'FFF', 789)
mine2(buz)

print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))
 
foo: 4, bar: 2, buz: 3
 

此时foo节点和buz节点链的内容如下。 你可以看到内容与中间不同。

In [29]:
pp.pprint(foo.chain)
 
[ { 'index': 1,
    'previous_hash': 1,
    'proof': 100,
    'timestamp': 1516245713.7215648,
    'transactions': []},
  { 'index': 2,
    'previous_hash': 'c97740db684709fd7455f413f0ad84f435236e1534caaea7cf744921b59fab3b',
    'proof': 35293,
    'timestamp': 1516245772.022711,
    'transactions': [{'amount': 1, 'recipient': 'bar', 'sender': '0'}]},
  { 'index': 3,
    'previous_hash': '8791fb38c957761c7af4331d65e834691cd7aa46019faaab3d655deae86d3dbb',
    'proof': 35089,
    'timestamp': 1516245803.1813366,
    'transactions': [ {'amount': 123, 'recipient': 'BBB', 'sender': 'AAA'},
                      {'amount': 1, 'recipient': 'foo', 'sender': '0'}]},
  { 'index': 4,
    'previous_hash': '99e21d9dd699d831803a0ea41d08cf9b2cfa642b94d4ee5ba4a38d1773c1c5c3',
    'proof': 119678,
    'timestamp': 1516245803.3608067,
    'transactions': [ {'amount': 456, 'recipient': 'DDD', 'sender': 'CCC'},
                      {'amount': 1, 'recipient': 'foo', 'sender': '0'}]}]
 
发表回复
你还没有登录,请先 登录或 注册!
话题作者
数据分析师