区块链(BlockChain)的相关知识,用Python做一简单实现
什么是区块链
简单来说,区块链就是把加密数据(区块)按照时间顺序进行叠加(链)生成的永久、不可逆向修改的记录。具体来说,它区块链是由一串使用密码学方法产生的数据块组成的,每一个区块都包含了上一个区块的哈希值(hash),从创始区块(genesis block)开始连接到当前区块,形成块链。每一个区块都确保按照时间顺序在上一个区块之后产生,否则前一个区块的哈希值是未知的。它是比特币的一个重要概念。
特点
区块链有如下特点:
去中心化:区块链不依赖于某个中心节点,而是依赖于分布式的各个节点。
无须信任系统:区块链中基于密码学算法,数据需要网络内其他用户的批准,所以不需要一套第三方中介结构或信任机构背书。
不可篡改和加密安全性:区块链采取单向哈希算法,同时每个新产生的区块严格按照时间线形顺序推进,时间的不可逆性导致任何试图入侵篡改区块链内数据 信息的行为都很容易被追溯,导致被其他节点的排斥,从而可以限制相关不法行为。
以上特点使得区块链在银行、证券市场和金融等诸多领域有着越来越多的应用。
区块链工作原理
区块链式一系列加密的数据块。这些区块由一个包含元数据的区块头和紧跟其后的构成区块主体的一长串交易组成。比特币中的区块结构如下:
区块头
区块头中包含了与区块链中其它区块中的连接信息、时间戳和nonce等信息,具体如下:
区块标识符
区块有两个标示符,一是区块头的哈希值,二是区块高度。区块头的哈希值是通过SHA256算法对区块头进行二次哈希计算而得到的数字。区块哈希值可以唯一、明确地标识一个区块,并且任何节点通过简单地对区块头进行哈希计算都可以独立地获取该区块哈希值。区块高度是指该区块在区块链中的位置。区块高度并不是唯一的标识符。虽然一个单一的区块总是会有一个明确的、固定的区块高度,但反过来却并不成立,一个区块高度并不总是识别一个单一的区块。两个或两个以上的区块可能有相同的区块高度,在区块链里争夺同一位置。
了解了以上基础后下面开始用Python实现一个简单的区块链。
区块链的Python实现
一、定义区块结构
In[1]:
# block.py
import hashlib
import uuid
class Block(object):
def __init__(self, data=None, previous_hash=None):
self.identifier = uuid.uuid4().hex # 产生唯一标示
self.nonce = None # nonce值
self.data = data # 区块内容
self.previous_hash = previous_hash # 父节点哈希值
def hash(self, nonce=None):
'''
计算区块的哈希值
'''
message = hashlib.sha256()
message.update(self.identifier.encode('utf-8'))
message.update(str(nonce).encode('utf-8'))
message.update(str(self.data).encode('utf-8'))
message.update(str(self.previous_hash).encode('utf-8'))
return message.hexdigest()
def hash_is_valid(self, the_hash):
'''
校验区块哈希值有否有效
'''
return the_hash.startswith('0000')
def __repr__(self):
return 'Block
以上就是一个区块结构,这里实现的是一个简化版,没有完全对应比特币中的区块。这里的区块包含一个唯一标识符、父节点的哈希值、nonce值和该区块的内容字段。可以看到一个区块的哈希值必须满足一定的条件才是有效的,比如以0000开始。下面对这个区块结构进行初始化。
In[2]:
# 创建一个内容为hello world的内容块
block = Block('Hello World')
block
Out[2]:
Block
以上一个区块虽然创建完成,但其哈希值不是有效的。
In[3]:
block.hash_is_valid(block.hash())
Out[3]:
False
改变nonce的值就可以得到一个新的哈希值。
In[4]:
block.hash(1)
Out[4]:
'57cf8f86dc9e371bee5edab82924d0b6d7ed14f7dfdaf57ef14935e054397813'
哈希值更新了,但还不是有效的哈希值。为了得到有效哈希值,是一个不断更新nonce值的过程,或者说一个挖矿(mine)过程。下面添加一个mine函数用来得到一个合适的nonce值。
In[5]:
# block.py
import hashlib
import uuid
class Block(object):
def __init__(self, data=None, previous_hash=None):
self.identifier = uuid.uuid4().hex # 产生唯一标示
self.nonce = None # nonce值
self.data = data # 区块内容
self.previous_hash = previous_hash # 父节点哈希值
def hash(self, nonce=None):
'''
计算区块的哈希值
'''
message = hashlib.sha256()
message.update(self.identifier.encode('utf-8'))
message.update(str(nonce).encode('utf-8'))
message.update(str(self.data).encode('utf-8'))
message.update(str(self.previous_hash).encode('utf-8'))
return message.hexdigest()
def hash_is_valid(self, the_hash):
'''
校验区块哈希值有否有效
'''
return the_hash.startswith('0000')
def __repr__(self):
return 'Block
'''
新增挖矿函数
'''
def mine(self):
# 初始化nonce为0
cur_nonce = self.nonce or 0
# 循环直到生成一个有效的哈希值
while True:
the_hash = self.hash(nonce=cur_nonce)
if self.hash_is_valid(the_hash): # 如果生成的哈希值有效
self.nonce = cur_nonce # 保持当前nonce值
break # 并退出
else:
cur_nonce += 1 # 若当前哈希值无效,更新nonce值,进行加1操作
In[6]:
block = Block('Hello World')
# 挖矿,循环直至找到合适的nonce
block.mine()
# 打印
block
Out[6]:
Block
至此,第一个有效的区块生成完成,下面开始定义区块链。
二、定义区块链结构
In[7]:
class BlockChain(object):
def __init__(self):
self.head = None # 指向最新的一个区块
self.blocks = {} # 包含所有区块的一个字典
'''
添加区块函数
'''
def add_block(self, new_block):
previous_hash = self.head.hash(self.head.nonce) if self.head else None
new_block.previous_hash = previous_hash
self.blocks[new_block.identifier] = {
'block': new_block,
'previous_hash': previous_hash,
'previous': self.head,
}
self.head = new_block
def __repr__(self):
num_existing_blocks = len(self.blocks)
return 'Blockchain<{} Blocks, Head: {}>'.format(
num_existing_blocks,
self.head.identifier if self.head else None
)
定义好区块链结构后,下面就开始初始化一条区块链。
In[8]:
# 初始化
chain = BlockChain()
# 打印
chain
Out[8]:
Blockchain<0 Blocks, Head: None>
In[9]:
# 添加区块
chain.add_block(block)
# 打印
chain
Out[9]:
Blockchain<1 Blocks, Head: 622654f485354465bfa0d998f3145ca7>
In[10]:
# 添加更多的区块
for i in range(6):
new_block = Block(i)
new_block.mine()
chain.add_block(new_block)
# 打印
chain
Out[10]:
Blockchain<7 Blocks, Head: 930b67933004422090add38b7616272a>
基于上面的内容实现一个简单的记账本功能。
三、简单记账本实现
记账本的功能如下:
实现基本的收支记录;
计算当前余额;
对收支情况做简单统计分析。
账单记录的格式如下:
日期|描述|金额
下面开始一步步实现上述功能。
1、定义收支记录
基于Block实现一个支持收支记录格式的类,代码如下:
In[12]:
from datetime import datetime
class AccountBill(Block):
def __init__(self, content, amount):
t = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
data = "{}|{}|{}".format(t, content, amount)
return super(AccountBill, self).__init__(data)
'''
获取金额数量
'''
def get_amount(self):
amount = 0
if self.data:
amount = int(self.data.split('|')[2])
return amount
def get_content(self):
content = ''
if self.data:
content = self.data.split('|')[1]
return content
def __repr__(self):
return 'Bill: {}>'.format(
self.data
)
In[13]:
# 创建记录
AccountBill('测试', 100)
Out[13]:
Bill: 2017-08-16 22:20:06|测试|100>
2、计算当前余额
上面已经定义了收支记录,接下来在BlockChain基础上定义一个方法用来计算当前余额。代码如下:
In[14]:
from collections import OrderedDict
class AccountBook(BlockChain):
def __init__(self):
self.head = None # 指向最新的一个区块
self.blocks = OrderedDict() # 包含所有区块的一个字典
'''
添加记录
'''
def add_block(self, new_bill):
new_bill.mine()
super(AccountBook, self).add_block(new_bill)
'''
计算当前余额
'''
def balance(self):
balance = 0
if self.blocks:
for k, v in self.blocks.items():
balance += v['block'].get_amount()
return balance
def __repr__(self):
num_existing_blocks = len(self.blocks)
return 'AccountBook<{} Bills, Head: {}>'.format(
num_existing_blocks,
self.head.identifier if self.head else None
)
In[15]:
# 创建几笔记录
book = AccountBook()
b1 = AccountBill('工资', 10000)
book.add_block(b1)
b2 = AccountBill('房租', -2500)
book.add_block(b2)
b3 = AccountBill('衣服', -1500)
book.add_block(b3)
b4 = AccountBill('吃饭', -1000)
book.add_block(b4)
b5 = AccountBill('股票收入', 200)
book.add_block(b5)
b6 = AccountBill('看电影', -200)
book.add_block(b6)
b7 = AccountBill('购物', -1000)
book.add_block(b7)
b8 = AccountBill('水电费等', -100)
book.add_block(b8)
In[16]:
# 计算当前余额
book.balance()
Out[16]:
3900
3、简单分析收支记录
In[17]:
# 打印收支记录
for k,v in book.blocks.items():
print(v['block'].data)
2017-08-16 22:21:09|工资|10000
2017-08-16 22:21:09|房租|-2500
2017-08-16 22:21:10|衣服|-1500
2017-08-16 22:21:10|吃饭|-1000
2017-08-16 22:21:10|股票收入|200
2017-08-16 22:21:11|看电影|-200
2017-08-16 22:21:11|购物|-1000
2017-08-16 22:21:11|水电费等|-100
In[18]:
# 使用柱状图展示收支记录
%matplotlib inline
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
# 初始化数据
x_data = [] # 金额
y_data = [] # 描述
colors = [] # 颜色
for k,v in book.blocks.items():
bill = v['block']
y_data.append(bill.get_content())
amount = bill.get_amount()
if amount > 0:
x_data.append(amount)
colors.append('blue')
else:
x_data.append(-amount)
colors.append('red')
y_pos = np.arange(len(y_data))
plt.bar(y_pos, x_data, align='center', alpha=0.5, color=colors)
plt.xticks(y_pos, y_data)
plt.ylabel('金额')
plt.title('收支记录')
plt.show()
In[19]:
# 简单分析支出组成
labels = []
amounts = []
colors = ['gold', 'yellowgreen', 'lightcoral', 'lightskyblue'] # 用不同颜色显示
for k,v in book.blocks.items():
bill = v['block']
amount = bill.get_amount()
# 只展示支出
if amount < 0:
labels.append(bill.get_content())
amounts.append(-amount)
plt.pie(amounts, labels=labels, colors=colors, shadow=True, autopct='%1.1f%%')
plt.axis('equal')
plt.show()
以上就是一个简单区块链,后面还会涉及到区块链的有效性。当区块链中一个区块被改变后,这个区块的哈希就会改变,从而影响到这块区块之后的区块,致使这个区块链不再有效。这些将在后续继续深入。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对爱站的支持。
上一篇:用Python实现一个区块链
下一篇:区块链技术在数字资产化领域的应用