区块链作为比特币和其他加密货币的核心技术,在最近几年引起了全世界的注意,但是各国这一颠覆性的技术态度不一,因为其去中心化的分布式结构,可以使用户之间直接进行交流,无需中心节点参与的这种技术模式对银行、证券等机构带来了极大影响。
区块链的技术模式和各国对区块链的态度:
而在本篇文章,抛开介绍区块链的技术特点和应用场景,文摘菌手把手的教大家如何用python实现一个基础的区块链,和一个区块链的客户端。
我们实现的区块链有如下几个特性:
-
可以向区块链中添加多个节点。
-
工作量证明(PoW)。
-
简单的节点间冲突解决机制。
-
使用RSA 加密进行交易。
我们的区块链客户端有如下几个功能:
-
使用公钥/私钥加密技术生成钱包。(基于RSA算法)。
-
使用RSA 加密算法生成交易。
我们还实现了2个展示界面:
-
挖矿者使用的“区块链前端”
-
用户生成钱包和发币的“区块链客户端”
我在原始代码的基础上进行了一些改动,向交易中加入了RSA加密,并实现了钱包生成和交易加密,两个界面使用HTML/CSS/JS 实现。
完整的项目代码:
https://github.com/adilmoujahid/blockchain-python-tutorial
请注意,这个实现是用于教学目的,所以它不适用于生产环境。因为它保密性不够,且缺乏一些重要的特性。
区块链客户端实现
你可以从终端启动区块链客户端。进入blockchain_client文件夹,并输入命令:python blockchain_client.py。
在浏览器中打开http://localhost:8080,接下来你会看到如下展示界面。
展示界面导航栏有3个标签:
-
钱包生成器:使用RSA加密算法生成钱包(公钥/私钥对)。
-
生成交易:生成交易并将其发送到区块链节点。
-
查看交易:查看区块链上的交易。
要想生成交易或查看交易,至少需要一个区块链节点在运行(将在下一节中介绍)。
blockchain_client.py文件代码中一些重要部分的说明:我们定义了一个Python类,我们命名了4个属性字段:sender_address,sender_private_key,recipient_address,value。
这是发送方创建交易所需的4个信息。
to_dict()方法返回一个Python字典格式交易信息(没有发件人的私钥)。
sign_transaction()方法接收交易信息(没有发件人的私钥),然后使用发送者的私钥进行签名。
class Transaction:
def __init__(self, sender_address, sender_private_key, recipient_address, value):
self.sender_address = sender_address
self.sender_private_key = sender_private_key
self.recipient_address = recipient_address
self.value = value
def __getattr__(self, attr):
return self.data[attr]
def to_dict(self):
return OrderedDict({'sender_address': self.sender_address,
'recipient_address': self.recipient_address,
'value': self.value})
def sign_transaction(self):
"""
Sign transaction with private key
"""
private_key = RSA.importKey(binascii.unhexlify(self.sender_private_key))
signer = PKCS1_v1_5.new(private_key)
h = SHA.new(str(self.to_dict()).encode('utf8'))
return binascii.hexlify(signer.sign(h)).decode('ascii')
下面是初始化一个Python Flask应用的代码行, 我们将用它来创建不同的API来与区块链及其客户进行交互。
app = Flask(__name__)
下面我们定义了3个返回HTML页面的Flask路径,其中每个标签都有一个html页面。
@app.route('/')
def index():
return render_template('./index.html')
@app.route('/make/transaction')
def make_transaction():
return render_template('./make_transaction.html')
@app.route('/view/transactions')
def view_transaction():
return render_template('./view_transactions.html')
下面我们定义一个生成钱包(私有/公钥对)的API。
@app.route('/wallet/new', methods=['GET'])
def new_wallet():
random_gen = Crypto.Random.new().read
private_key = RSA.generate(1024, random_gen)
public_key = private_key.publickey()
response = {
'private_key': binascii.hexlify(private_key.exportKey(format='DER')).decode('ascii'),
'public_key': binascii.hexlify(public_key.exportKey(format='DER')).decode('ascii')
}
return jsonify(response), 200
下面我们定义一个API,将sender_address, sender_private_key, recipient_address, value字段作为输入,并返回交易(没有私钥)和签名。
@app.route('/generate/transaction', methods=['POST'])
def generate_transaction():
sender_address = request.form['sender_address']
sender_private_key = request.form['sender_private_key']
recipient_address = request.form['recipient_address']
value = request.form['amount']
transaction = Transaction(sender_address, sender_private_key, recipient_address, value)
response = {'transaction': transaction.to_dict(), 'signature': transaction.sign_transaction()}
return jsonify(response), 200
![0723f7646a0bf3f4ea8c16f5473e32ecebd1329b](https://yqfile.alicdn.com/0723f7646a0bf3f4ea8c16f5473e32ecebd1329b.png?x-oss-process=image/resize,w_1400/format,webp)
区块链的实现
你可以从终端启动区块链节点,通过进入blockchain文件夹,并输入命令:python blockchain_client.py或python blockchain_client.py -p <PORT NUMBER> 。如果你未指定端口号,则会默认端口号为5000。在浏览器中打开http://localhost:<PORT NUMBER>可以看到区块链前端展示界面。
展示界面导航栏有两个标签:
-
挖掘:用于查看交易和区块链数据,以及挖掘新的交易区块。
-
配置:用于配置不同区块链节点之间的连接。
下面是blockchain.py文件代码中一些重要部分的说明。
我们首先定义一个具有以下属性的Blockchain类:
-
transactions:将被添加到下一区块的交易列表。
-
chain::实际的区块链,也就是一个区块数组。
-
nodes:一个包含节点URL的集合。区块链使用这些节点从其他节点中检索区块链数据并且在检查到它们没有同步时更新其区块链。
-
node_id:一个标识blockchain节点的随机字符串。
这个Blockchain类还实现了以下方法:
-
register_node(node_url): 将新的区块链节点添加到节点列表中。
-
verify_transaction_signature(sender_address, signature, transaction): 检查提供的签名是否与通过公钥(sender_address)签署的交易相符。
-
submit_transaction(sender_address, recipient_address, value, signature): 如果签名通过验证,则将交易添加到交易列表中。
-
create_block(nonce, previous_hash):向区块链添加一个交易块。
-
hash(block): 创建一个区块的SHA-256散列。
-
proof_of_work():工作算法的证明。寻找满足挖掘条件的随机数。
-
valid_proof(transactions, last_hash, nonce, difficulty=MINING_DIFFICULTY):检查散列值是否满足挖掘条件。该函数在proof_of_work函数中使用。
-
valid_chain(chain): 检查区块链是否有效。
-
resolve_conflicts():通过用网络中最长链代替链的方法解决区块链节点之间的冲突。
class Blockchain:
def __init__(self):
self.transactions = []
self.chain = []
self.nodes = set()
#Generate random number to be used as node_id
self.node_id = str(uuid4()).replace('-', '')
#Create genesis block
self.create_block(0, '00')
def register_node(self, node_url):
"""
Add a new node to the list of nodes
"""
...
def verify_transaction_signature(self, sender_address, signature, transaction):
"""
Check that the provided signature corresponds to transaction
signed by the public key (sender_address)
"""
...
def submit_transaction(self, sender_address, recipient_address, value, signature):
"""
Add a transaction to transactions array if the signature verified
"""
...
def create_block(self, nonce, previous_hash):
"""
Add a block of transactions to the blockchain
"""
...
def hash(self, block):
"""
Create a SHA-256 hash of a block
"""
...
def proof_of_work(self):
"""
Proof of work algorithm
"""
...
def valid_proof(self, transactions, last_hash, nonce, difficulty=MINING_DIFFICULTY):
"""
Check if a hash value satisfies the mining conditions. This function is used within the proof_of_work function.
"""
...
def valid_chain(self, chain):
"""
check if a bockchain is valid
"""
...
def resolve_conflicts(self):
"""
Resolve conflicts between blockchain's nodes
by replacing our chain with the longest one in the network.
"""
...
下面这一行,我们初始化了一个Python Flask 应用,用于创建和区块链交互的API。
app = Flask(__name__)
CORS(app)
下面,我们初始化一个区块链对象。
blockchain = Blockchain()
下面我们定义了2种返回我们区块链前端展示界面html页面的Flask路线。
@app.route('/')
def index():
return render_template('./index.html')
@app.route('/configure')
def configure():
return render_template('./configure.html')
下面我们定义了Flask API来管理交易和挖掘区块链。
此API将'sender_address', 'recipient_address', 'amount' 和 'signature' 作为输入,并且如果签名有效,则将交易添加到将添加到下一个块的交易列表中。
-
'/transactions/get':此API返回所有将会添加到下一个块的交易。
-
'/chain':此API返回所有区块链数据。
-
'/mine': 此API运行工作算法的证明,同时添加新的交易块到区块链。
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
values = request.form
# Check that the required fields are in the POST'ed data
required = ['sender_address', 'recipient_address', 'amount', 'signature']
if not all(k in values for k in required):
return 'Missing values', 400
# Create a new Transaction
transaction_result = blockchain.submit_transaction(values['sender_address'], values['recipient_address'], values['amount'], values['signature'])
if transaction_result == False:
response = {'message': 'Invalid Transaction!'}
return jsonify(response), 406
else:
response = {'message': 'Transaction will be added to Block '+ str(transaction_result)}
return jsonify(response), 201
@app.route('/transactions/get', methods=['GET'])
def get_transactions():
#Get transactions from transactions pool
transactions = blockchain.transactions
response = {'transactions': transactions}
return jsonify(response), 200
@app.route('/chain', methods=['GET'])
def full_chain():
response = {
'chain': blockchain.chain,
'length': len(blockchain.chain),
}
return jsonify(response), 200
@app.route('/mine', methods=['GET'])
def mine():
# We run the proof of work algorithm to get the next proof...
last_block = blockchain.chain[-1]
nonce = blockchain.proof_of_work()
# We must receive a reward for finding the proof.
blockchain.submit_transaction(sender_address=MINING_SENDER, recipient_address=blockchain.node_id, value=MINING_REWARD, signature="")
# Forge the new Block by adding it to the chain
previous_hash = blockchain.hash(last_block)
block = blockchain.create_block(nonce, previous_hash)
response = {
'message': "New Block Forged",
'block_number': block['block_number'],
'transactions': block['transactions'],
'nonce': block['nonce'],
'previous_hash': block['previous_hash'],
}
return jsonify(response), 200
下面我们定义Flask API来管理区块链节点。
-
'/nodes/register':此API将节点URL列表作为输入,同时添加URL到节点列表。
-
'/nodes/resolve':此API通过使用网络中最长的可用链替代本地链的方式解决区块链节点间的冲突。
-
'/nodes/get':此API返回节点列表。
@app.route('/nodes/register', methods=['POST'])
def register_nodes():
values = request.form
nodes = values.get('nodes').replace(" ", "").split(',')
if nodes is None:
return "Error: Please supply a valid list of nodes", 400
for node in nodes:
blockchain.register_node(node)
response = {
'message': 'New nodes have been added',
'total_nodes': [node for node in blockchain.nodes],
}
return jsonify(response), 201
@app.route('/nodes/resolve', methods=['GET'])
def consensus():
replaced = blockchain.resolve_conflicts()
if replaced:
response = {
'message': 'Our chain was replaced',
'new_chain': blockchain.chain
}
else:
response = {
'message': 'Our chain is authoritative',
'chain': blockchain.chain
}
return jsonify(response), 200
@app.route('/nodes/get', methods=['GET'])
def get_nodes():
nodes = list(blockchain.nodes)
response = {'nodes': nodes}
return jsonify(response), 200
![d7daa29240f74ef871fe48e83f96287c7d9dc6d0](https://yqfile.alicdn.com/d7daa29240f74ef871fe48e83f96287c7d9dc6d0.png?x-oss-process=image/resize,w_1400/format,webp)
结论
在此篇文章中,我们介绍了涉及区块链背后一些核心概念,并且学习如何用Python实现一个区块链。为了简单起见,此文没有涉及一些技术细节,例如:钱包地址和Merkle树。如果你想了解有关该主题的更多信息,我建议阅读比特币白皮书原著,并跟着比特币维基和Andreas Antonopoulos的优秀书籍学习:掌握比特币:编程开放区块链。
原文发布时间为:2018-04-4
本文作者:文摘菌