構建區(qū)塊鏈的同時進行學習|編程|使用在POSTD中發(fā)布的解釋和代碼逐步進行測試。
直接由Jupyter實施,因為覺得在Flask中的實現(xiàn)很多余。
閱讀文本是假定您對于塊鏈本身的機制已經(jīng)有所理解。
代碼的實現(xiàn)¶1.創(chuàng)建一個區(qū)塊鏈類¶
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)
自身節(jié)點的標識符如下。
In [5]:
- import pprint
- pp = pprint.PrettyPrinter(indent=2)
'7d10057a10364156aa9ac7b92ce3c34e'
一旦實例化,將創(chuàng)建第一個區(qū)塊
- index:索引為1
- previous_hash:初始的hash值1
- length:鏈條的長度顯然是1
In [6]:
- b = Blockchain()
- pp.pprint(full_chain(b))
開始創(chuàng)建第一個街區(qū)
- { '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'}]}
在交易環(huán)節(jié)中,只描述了采礦挖掘交易
- sender:發(fā)件人為0
- recipient:收件人標識符
- amount:金額是1
記錄為交易。 現(xiàn)在我們來看看整個區(qū)塊鏈的內容
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}
作為一個新的交易(交易),有的區(qū)塊只包含挖掘結果。
我們在這里添加一個新的事務。
- 添加發(fā)件人到'foo'
- 將收件人設置為'bar'
- amount 設置為10
并達成交易。
In [9]:
- index = b.new_transaction('foo', 'bar', 10)
此時index(塊的索引)是3。 上述交易存儲在這個塊中。
In [10]:
- print(index)
此時,從整個鏈條來看,上面添加的交易還沒有在鏈上注冊。
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)
創(chuàng)建第3個區(qū)塊,保存之前創(chuàng)建的事務信息和挖掘信息。
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'}]}
此時整個鏈條的狀態(tài)如下。
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()類的實現(xiàn)¶
實現(xiàn)Blcokchain 2類包含共識算法。
另外,節(jié)點標識符被保存為一個類成員,并且在挖掘它時被修改為使用它。 (為了能夠處理多個節(jié)點的塊鏈)
(實際上,定義Node類并將Blockchain 2類作為has-a作為成員似乎更好,但是因為在Blockchain類的原始版本中引入了register_node()或resolve_conflicts()
In [15]:import copyBlockchainNeighbours = {}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
創(chuàng)建多個節(jié)點的塊鏈¶
為三個節(jié)點創(chuàng)建一個塊鏈,并注冊為一個相鄰節(jié)點。
In [35]:# 為節(jié)點標識符為“foo”,“bar”,“buz”的三個節(jié)點創(chuàng)建一個塊鏈。foo = Blockchain2('foo')bar = Blockchain2('bar')buz = Blockchain2('buz')# 注冊在相鄰節(jié)點的列表中BlockchainNeighbours['foo'] = fooBlockchainNeighbours['bar'] = barBlockchainNeighbours['buz'] = buz# 'bar','buz'注冊為'foo'節(jié)點鄰居foo.register_node('bar')foo.register_node('buz')# 為“bar”,“buz”節(jié)點注冊鄰居bar.register_node('foo')bar.register_node('buz')buz.register_node('foo')buz.register_node('bar')
在初始狀態(tài)下,所有節(jié)點的鏈路長度為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
即使你試圖在初始狀態(tài)下解決沖突,也沒有鏈節(jié)點長于foo節(jié)點,所以foo節(jié)點鏈不會改變。
In [19]:
foo.resolve_conflicts()
node id: buz, len: 1node id: bar, len: 1
Out[19]:
False
在一些節(jié)點上伸展塊¶
接下來,在bar節(jié)點上挖掘并添加一個塊。
In [20]:
pp.pprint(mine2(bar))
{ 'index': 2, 'message': 'New Block Forged', 'previous_hash': 'c97740db684709fd7455f413f0ad84f435236e1534caaea7cf744921b59fab3b', 'proof': 35293, 'transactions': [{'amount': 1, 'recipient': 'bar', 'sender': '0'}]}
節(jié)點的長度只有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': []}]
消除節(jié)點之間的沖突¶
在這種狀態(tài)下,當試圖解決foo節(jié)點處的沖突時,foo節(jié)點鏈被(更長的)節(jié)點鏈覆蓋。
In [25]:
foo.resolve_conflicts()node id: buz, len: 1node id: bar, len: 2Replacing `foo' <- `bar'
Out[25]:
True
當沖突的解決完成時,foo節(jié)點的鏈長變?yōu)?。
In [26]:
print('foo: %d, bar: %d, buz: %d' %(len(foo.chain), len(bar.chain), len(buz.chain)))foo: 2, bar: 2, buz: 1
如果每個節(jié)點鏈的內容不同¶
接下來,考慮每個節(jié)點鏈的內容不同的情況。
這里我們看到foo節(jié)點和buz節(jié)點的內容不同的情況。
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: 2node id: bar, len: 2Replacing `buz' <- `foo'foo: 2, bar: 2, buz: 2
在這里,在foo節(jié)點添加兩個塊,一個塊在buz節(jié)點,并且添加具有不同事務的塊。
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節(jié)點和buz節(jié)點鏈的內容如下。 你可以看到內容與中間不同。
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'}]}]