诗人都在海底

2018-10-08   阅读量: 1127

Python编程 区块链

牛刀小试——边构建区块链边学习(Python实现)

扫码加入数据分析学习群

在构建区块链的同时进行学习,阅读本文是假定您对区块链本身的机制已经有一定的理解。

代码实现(想获取notebook的话可以在评论留言)

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.挖矿代码

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.添加示例并返回整个链

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

尝试移动块链

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

import pprint
pp = pprint.PrettyPrinter(indent=2)

自身节点的标识符如下。

node_identifier

'7d10057a10364156aa9ac7b92ce3c34e'

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

index:索引为1

previous_hash:初始的hash值1

length:链条的长度显然是1

b = Blockchain()
pp.pprint(full_chain(b))

{ 'chain': [ { 'index': 1,

'previous_hash': 1,

'proof': 100,

'timestamp': 1516245610.8226993,

'transactions': []}],

'length': 1}

开始创建第一个区块

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

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

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

并达成交易。

index = b.new_transaction('foo', 'bar', 10)

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

print(index)

3

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

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}

挖矿并添加一个新的块。

newblock = mine(b)

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

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'}]}

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

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()类的实现包含共识算法。另外,节点标识符被保存为一个类成员,并且在挖掘它时被修改为使用它。 (为了能够处理多个节点的块链)

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


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

创建多个节点的块链

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

# 为节点标识符为“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。

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

foo: 1, bar: 1, buz: 1

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

foo.resolve_conflicts()
node id: buz, len: 1
node id: bar, len: 1

False

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

pp.pprint(mine2(bar))

{ 'index': 2,

'message': 'New Block Forged',

'previous_hash': 'c97740db684709fd7455f413f0ad84f435236e1534caaea7cf744921b59fab3b',

'proof': 35293,

'transactions': [{'amount': 1, 'recipient': 'bar', 'sender': '0'}]}

节点的长度只有2

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

foo: 1, bar: 2, buz: 1

pp.pprint(foo.chain)

[ { 'index': 1,

'previous_hash': 1,

'proof': 100,

'timestamp': 1516245713.7215648,

'transactions': []}]

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'}]}]

pp.pprint(buz.chain)

[ { 'index': 1,

'previous_hash': 1,

'proof': 100,

'timestamp': 1516245713.7215648,

'transactions': []}]

消除节点之间的冲突

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

foo.resolve_conflicts()
node id: buz, len: 1
node id: bar, len: 2
Replacing `foo' <- `bar'

True

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

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

foo: 2, bar: 2, buz: 1

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

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

这里我们看到foo节点和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节点,并且添加具有不同事务的块。

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节点链的内容如下。 你可以看到内容与中间不同。

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'}]}]

如果你觉得有用欢迎点赞获得PGC,也可以关注我~

385.0862 11 1 关注作者 收藏

评论(0)


暂无数据

推荐课程

推荐帖子