本文主要参考用Python从零开始创建区块链 | 登链社区 | 区块链技术社区 (learnblockchain.cn)
区块链作为一种新型去中心化分布式数据系统,确保了数字加密货币交易等数据的安全存储。其不可篡改、公开透明、匿名的特性降低了互联网信任成本,助力实现从信息互联网向价值互联网的跨越。基于区块链技术,我开发的山寨比特币交易系统提供了一种安全且可验证的交易方式。在系统中,各方共享信息并达成具有预定义规则的协议。本文对山寨比特币交易系统主要工作内容如下:
山寨比特币交易系统以Python语言为基础,构建了一个分布式数字货币交易平台。该系统主要包括区块链、节点、交易、工作量证明和共识算法等核心模块。区块链模块以时间顺序打包交易记录,形成链式结构,确保数据安全和不间断;节点模块维护区块链同步和扩展,通过分布式共识算法确保数据一致性;交易模块支持数字货币发送、接收和交易,保障交易安全性和隐私;工作量证明模块模拟比特币工作量证明算法,确保交易安全性和不可篡改;共识算法模块在多个节点间达成共识,实现交易快速高效。
山寨比特币交易系统为用户提供安全、可靠、可验证的数字货币交易方式,有望在数字经济领域发挥重要作用。
-
原理阐述
在本项目中,我通过定义一个`Blockchain`类来模拟区块链。在类的构造方法中,我将调用已封装的创建区块方法`new_block()`,生成一个创世区块。同时,我设置该区块的前一个哈希值为1,以表明其为创世区块。此外,我使用列表来保存当前交易和链,并采用集合(set)来存储分布式的节点(node)。
为了模拟分布式,我将注册节点的方法封装到register_node()方法中;为了实现共识算法,我将“检查一条链是否有效”的方法和“找出最长连”的方法分别封装为valid_chain() 和 resolve_conflicts()。
然后创建区块和创建新的交易分别对应new_block() 方法和 new_transaction()方法。
模拟工作量证明时,我将会计算前一个区块的hash值,这部分内容我封装在了 proof_of_work() 方法中。
鉴于我的项目采用Web架构,并需运用Flask框架,在处理请求方法时,调用`Blockchain`类中的方法以模拟交易。
-
需求分析
本项目是一个基于Python的山寨比特币交易系统,实现了区块链中的部分基本算法,如共识算法等,并具备创建交易、打包区块、工作量证明等核心功能。通过模仿区块链的分布式特性,实现了高性能和安全性。该项目采用Web架构,并以Python的Flask框架为基础。通过定义一个模拟区块链操作的类,实现了与Flask的交互。在测试过程中,我们使用了Postman这款API测试工具。
-
系统实现
1 功能设计
1.1 初始化区块链
在Blockchain的构造函数中,初始化当前交易,当前链,以及当前节点,同时创建一个创世区块,设置previous_hash 的值为1表示当前区块为创世区块。
- # 定义一个区块链类
- class Blockchain:
- def __init__(self): # 初始化当前交易、区块链、节点和创世区块
- self.current_transactions = [] # 当前交易
- self.chain = [] # 一条链
- self.nodes = set() # 用set来存储node节点
- self.new_block(previous_hash='1', proof=100) # 创建一个创世区块
1.2 注册节点
为的是模拟比特币的分布式交易系统,系统中可能总是存在多个节点,这些节点都知道对方的存在。在这里我们通过将Web服务启动在不同的端口,来模拟分布式交易系统中的多个不同节点。
让一个节点知道其他节点的存在,这里我们使用的方法是将另一个节点注册到当前节点中来,即将另一个节点的信息存放在当前节点Blockchain类中的 nodes 属性中。
我们这里模拟注册节点的方法是register_node() 函数,我们需要传入另一个节点的url地址,然后解析得到服务器路径,再存入nodes集合中。
- def register_node(self, address): # 注册网络中的节点
- parsed_url = urlparse(address) # 将一个url的字符串解析并返回一个url的对象
- if parsed_url.netloc: # 如果地址包含域名
- self.nodes.add(parsed_url.netloc) # 添加域名
- elif parsed_url.path: # 如果地址包含路径
- self.nodes.add(parsed_url.path) # 添加路径
- else:
- raise ValueError('Invalid URL') # 如果地址无效,抛出异常
1.3 创建交易
有了一个创世区块之后,我们就可以在这个创世区块中进行交易了,我们使用new_transaction() 函数来模拟一次交易,传入的参数:sender表示发送者的地址,recipient表示接受者的地址,amount表示数量,该函数的返回值是本次交易的index索引,这些信息最终都会被打包添加到下一个区块中。

- def new_transaction(self, sender, recipient, amount): # 创建一笔新的交易
- # 创建一个新的交易到下一个区块中
- self.current_transactions.append({
- 'sender': sender,
- 'recipient': recipient,
- 'amount': amount,
- })
- return self.last_block['index'] + 1 # 返回下一个区块的索引
1.4 工作量证明
我们知道,区块链中的每一个新的区块都来自于工作量证明,工作量证明的目标是计算出一串解决问题的数字,这个结果是十分难计算的,但是却十分容易验证,网络上的任何人都可以验证这个结果。
这里我们也用Python代码实现一个简单的工作量证明算法,我们的规则是:找到一个数字P,使得它与前一个区块的proof拼接而成的字符串的Hash值以4个零开头。
- # 工作量证明
- def proof_of_work(self, last_block):
- # 简单工作量证明算法
- # (1) 找出一个数字 P',使得 hash(PP') 包含前导的4个0
- # (2) 其中 P 是前一个证明, P' 是新的证明
- last_proof = last_block['proof']
- last_hash = self.hash(last_block)
- proof = 0
- while self.valid_proof(last_proof, proof, last_hash) is False:
- proof += 1
- return proof
- # 验证工作量
- @staticmethod
- def valid_proof(last_proof, proof, last_hash):
- # 验证数据
- # :param last_proof: <int> 前一个证明
- # :param proof: <int> 当前证明
- # :param last_hash: 前一个区块的哈希值
- # :return: <bool> 如果正确,返回 True;否则返回 False
- guess = f'{last_proof}{proof}{last_hash}'.encode()
- guess_hash = hashlib.sha256(guess).hexdigest()
- return guess_hash[:4] == "0000"
1.5 打包生成一个新的区块
这个过程我们需要模拟“挖矿”,通过Flask服务端,我们先运行一个工作量证明算法,得到下一个证明。
找到一个工作量证明之后,我们将给我们的“矿工”一定数量的“仿比特币”作为奖励,然后将当前所有的交易打包并生成一个新的区块,同时将存储当前交易的变量 current_transaction 重置,方便进行一下次交易。
这里我们将分别展示Flask服务端的代码以及Blockchain类中创建区块的代码。
ØFlask服务端的代码
- # 挖掘区块
- @app.route('/mine', methods=['GET'])
- def mine():
- # 我们运行这个工作证明算法,得到下一个证明
- last_block = blockchain.last_block
- proof = blockchain.proof_of_work(last_block)
- # 找到一个工作量证明之后,我们必须得到一个奖励
- blockchain.new_transaction(
- sender="0",
- recipient=node_identifier,
- amount=1,
- )
- # 添加到链中,生成一个新的区块
- 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 jsonify(response), 200
ØBlockchain类中的代码
- def new_block(self, proof, previous_hash): # 创建一个新的区块
- # 在区块链中创建一个新的区块
- block = {
- 'index': len(self.chain) + 1, # 区块索引
- 'timestamp': time(), # 时间戳
- 'transactions': self.current_transactions, # 交易列表
- 'proof': proof, # 工作量证明
- # 建立创世区块的时候,前一个区块的哈希值没有,所以这里我们需要从外部传入一个指定的 hash 值
- 'previous_hash': previous_hash or self.hash(self.chain[-1]), # 前一个区块的哈希
- }
- self.current_transactions = [] # 重置当前交易列表
- self.chain.append(block) # 将区块添加到链中
- return block
1.6 共识算法
由于我们的仿比特币交易系统是分布式的,所以我们必须保证所有的节点都运行在同一条链上,当一个节点与另一个节点有不同时就会存在冲突,为了解决这个问题,我们遵循最长链原则,使用共识算法,让网络中的节点达成共识。
共识算法的第一部分,我们需要检查一条链是否是有效链,
- def valid_chain(self, chain): # 共识算法,检查一条链是否有效
- last_block = chain[0] # 选取链中的第一个区块
- current_index = 1 # 当前索引初始化为1
- while current_index < len(chain): # 遍历区块链直到最后一个区块
- block = chain[current_index] # 获取当前索引的区块
- print(f'{last_block}') # 打印上一个区块
- print(f'{block}') # 打印当前区块
- print("\n-----------\n")
- # 检查哈希值和工作量证明
- last_block_hash = self.hash(last_block) # 获取上一个区块的哈希值
- if block['previous_hash'] != last_block_hash: # 检查当前区块的前一个哈希是否匹配
- return False
- # 检查工作量证明是否正确
- if not self.valid_proof(last_block['proof'], block['proof'], last_block_hash):
- return False
- last_block = block # 更新上一个区块为当前区块
- current_index += 1 # 索引加1,移动到下一个区块
- return True
- # 共识算法,找出一个最长链
- def resolve_conflicts(self):
- # 共识算法,负责循环读取所有相邻节点,获取它们的链并使用上面的方法验证它们的有效性。如果找到了一个更长的有效链,则取代我们当前的链。
- neighbours = self.nodes # 获取网络中的所有节点
- new_chain = None # 初始化新区块链变量
- max_length = len(self.chain) # 初始化最长链长度为当前链长度
- for node in neighbours: # 遍历所有节点
- response = requests.get(f'http://{node}/chain') # 向节点请求链信息
- if response.status_code == 200: # 如果请求成功
- length = response.json()['length'] # 获取链的长度
- chain = response.json()['chain'] # 获取链的数据
- if length > max_length and self.valid_chain(chain): # 如果找到更长的有效链
- max_length = length # 更新最长链长度
- new_chain = chain # 更新新区块链
- if new_chain: # 如果找到了新的区块链
- self.chain = new_chain # 替换当前链
- return True # 返回True表示链被替换了
- return False # 如果没有找到新的链,返回False
2 流程设计
2.1 环境准备
确保已经安装 Python3.9、pip、Flask、requests等相关环境。同时还需要一个 HTTP 客户端Postman。
2.2 初始化区块链
我们需要构造一个创世块(没有前区块的第一个区块),之后需要给它加上一个工作量证明。每个区块都需要经过工作量证明,俗称挖矿
- class Blockchain(object):
2.3 注册节点
使用register_node() 函数创建一个节点,作为我们连入区块链的服务器
- def register_node(self, address):
2.4 创建交易,并用区块分批保存交易
向列表中添加一个交易记录,并返回该记录将被添加到的区块(下一个待挖掘的区块)的索引,等下在用户提交交易时会有用。
2.5 将区块加入区块链
通过逐个添加方式,将区块一个个链接起来,返回整条链
2.6 实现工作量证明算法
通过新增一个交易授予矿工(自己)一个币
构造新区块并将其添加到链中
2.7 设立共识机制
当一个节点与另一个节点有不同的链时,就会产生冲突。 为了解决这个问题,我们将制定最长的有效链条是最权威的规则。换句话说就是:在这个网络里最长的链就是最权威的。 我们将使用这个算法,在网络中的节点之间达成共识。
2.8 通过端口调用服务
2.9 运行多个节点,进行测试
3 编程代码
关于Blockchain类的一些实现具体功能的函数,在4.1小结已经基本展示完毕,这里展示一些Flask服务端的代码。
Ø实例化Flask结点,Blockchain对象
- app = Flask(__name__)
- blockchain = Blockchain()
Ø创建一个新的交易
- # 创建一个新的交易
- @app.route('/transactions/new', methods=['POST'])
- def new_transaction():
- values = request.get_json()
- # 检查post数据
- required = ['sender', 'recipient', 'amount']
- if not all(k in values for k in required):
- return '缺少必要字段', 400
- # 创建一个新的交易
- index = blockchain.new_transaction(values['sender'], values['recipient'], values['amount'])
- response = { 'message': f'Transaction will be added to Block {index}'}
- return jsonify(response), 201
Ø返回整条链
- # 返回整条链
- @app.route('/chain', methods=['GET'])
- def full_chain():
- response = {
- 'chain': blockchain.chain,
- 'length': len(blockchain.chain),
- }
- return jsonify(response), 200
Ø添加相邻节点
- # 添加相邻节点
- @app.route('/nodes/register', methods=['POST'])
- def register_nodes():
- values = request.get_json()
- nodes = values.get('nodes')
- if nodes is None:
- return "错误!请提供一个有效的节点列表", 400
- for node in nodes:
- blockchain.register_node(node)
- response = {
- 'message': '新的节点已经被创建',
- 'total_nodes': list(blockchain.nodes),
- }
- return jsonify(response), 201
Ø共识机制
- # 共识机制
- @app.route('/nodes/resolve', methods=['GET'])
- def consensus():
- replaced = blockchain.resolve_conflicts()
- # app.run(host='10.210.5.208', port=port)
- if replaced:
- response = {
- 'message': '我们的链被替换了',
- 'new_chain': blockchain.chain
- }
- else:
- response = {
- 'message': '我们的链是权威的',
- 'chain': blockchain.chain
- }
- return jsonify(response), 200
Ø启动服务,指定端口
- if __name__ == '__main__':
- from argparse import ArgumentParser
- parser = ArgumentParser()
- parser.add_argument('-p', '--port', default=5000, type=int)
- args = parser.parse_args()
- port = args.port
- app.run(host='0.0.0.0',port=port)
4.界面实现
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>区块链演示</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f4f4f9;
color: #333;
margin: 0;
padding: 0;
}
header {
background-color: #8A2BE2;
color: #fff;
padding: 20px 0;
text-align: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.container {
width: 80%;
margin: 20px auto;
overflow: hidden;
}
.content {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.card {
background: #fff;
margin: 10px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border-radius: 8px;
flex: 1 1 calc(48% - 40px);
box-sizing: border-box;
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
}
.card h3 {
margin-top: 0;
color: #8A2BE2;
}
.card form {
display: flex;
flex-direction: column;
}
.card form input, .card form button {
padding: 10px;
margin: 5px 0;
font-size: 16px;
border: 1px solid #ddd;
border-radius: 4px;
}
.card form button {
background-color: #8A2BE2;
color: #fff;
border: none;
cursor: pointer;
transition: background-color 0.3s ease;
}
.card form button:hover {
background-color: #7a22c4;
}
.chain, .transactions, .node-chain {
margin: 20px 0;
}
.block, .transaction {
background: #fff;
margin: 10px 0;
padding: 10px;
border-left: 5px solid #8A2BE2;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.block p, .transaction p {
margin: 5px 0;
}
h2 {
color: #8A2BE2;
}
input[type="text"], input[type="number"] {
width: calc(100% - 22px);
}
button {
width: calc(100% - 22px);
}
</style>
</head>
<body>
<header>
<h1>区块链演示</h1>
</header>
<div class="container">
<div class="content">
<div class="card">
<h3>在5000端口挖掘新的区块</h3>
<button onclick="mineBlock('5000')">挖掘</button>
</div>
<div class="card">
<h3>在5000端口创建新的交易</h3>
<form id="transactionForm5000">
<input type="text" id="sender5000" placeholder="发送方">
<input type="text" id="recipient5000" placeholder="接收方">
<input type="number" id="amount5000" placeholder="金额">
<button type="button" onclick="createTransaction('5000')">创建交易</button>
</form>
</div>
<div class="card">
<h3>在其他端口挖掘新的区块</h3>
<input type="text" id="portMine" placeholder="端口号">
<button onclick="mineBlock(document.getElementById('portMine').value)">挖掘</button>
</div>
<div class="card">
<h3>在其他端口创建新的交易</h3>
<form id="transactionFormOther">
<input type="text" id="portTransaction" placeholder="端口号">
<input type="text" id="senderOther" placeholder="发送方">
<input type="text" id="recipientOther" placeholder="接收方">
<input type="number" id="amountOther" placeholder="金额">
<button type="button" onclick="createTransaction(document.getElementById('portTransaction').value)">创建交易</button>
</form>
</div>
<div class="card">
<h3>查看5000端口交易记录</h3>
<button onclick="fetchTransactions('5000')">查看</button>
</div>
<div class="card">
<h3>查看其他端口交易记录</h3>
<input type="text" id="portTransactions" placeholder="端口号">
<button onclick="fetchTransactions(document.getElementById('portTransactions').value)">查看</button>
</div>
<div class="card">
<h3>查看节点区块链</h3>
<form id="nodeChainForm">
<input type="text" id="nodeChain" placeholder="节点URL">
<button type="button" onclick="fetchNodeChain()">查看</button>
</form>
</div>
</div>
<div class="chain" id="chain"></div>
<div class="transactions" id="transactions"></div>
<div class="node-chain" id="node-chain"></div>
</div>
<script>
const apiBase = 'http://localhost';
async function fetchChain() {
const response = await fetch(`${apiBase}:5000/chain`);
const data = await response.json();
const chainDiv = document.getElementById('chain');
chainDiv.innerHTML = '<h2>当前区块链</h2>';
data.chain.forEach(block => {
const blockDiv = document.createElement('div');
blockDiv.className = 'block';
blockDiv.innerHTML = `
<p><strong>区块:</strong> ${block.index}</p>
<p><strong>时间戳:</strong> ${block.timestamp}</p>
<p><strong>交易:</strong> ${JSON.stringify(block.transactions)}</p>
<p><strong>工作量证明:</strong> ${block.proof}</p>
<p><strong>上一个区块哈希:</strong> ${block.previous_hash}</p>
`;
chainDiv.appendChild(blockDiv);
});
}
async function mineBlock(port) {
try {
const response = await fetch(`${apiBase}:${port}/mine`);
const data = await response.json();
alert(data.message);
if (port === '5000') {
fetchChain();
}
} catch (error) {
alert(`无法连接到端口 ${port}: ${error.message}`);
}
}
async function createTransaction(port) {
const sender = document.getElementById(`sender${port === '5000' ? '5000' : 'Other'}`).value;
const recipient = document.getElementById(`recipient${port === '5000' ? '5000' : 'Other'}`).value;
const amount = document.getElementById(`amount${port === '5000' ? '5000' : 'Other'}`).value;
try {
const response = await fetch(`${apiBase}:${port}/transactions/new`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ sender, recipient, amount })
});
const data = await response.json();
alert(data.message);
} catch (error) {
alert(`无法连接到端口 ${port}: ${error.message}`);
}
}
async function fetchTransactions(port) {
try {
const response = await fetch(`${apiBase}:${port}/chain`);
const data = await response.json();
const transactionsDiv = document.getElementById('transactions');
transactionsDiv.innerHTML = `<h2>${port}端口交易记录</h2>`;
data.chain.forEach(block => {
block.transactions.forEach(transaction => {
const transactionDiv = document.createElement('div');
transactionDiv.className = 'transaction';
transactionDiv.innerHTML = `
<p><strong>发送方:</strong> ${transaction.sender}</p>
<p><strong>接收方:</strong> ${transaction.recipient}</p>
<p><strong>金额:</strong> ${transaction.amount}</p>
`;
transactionsDiv.appendChild(transactionDiv);
});
});
} catch (error) {
alert(`无法连接到端口 ${port}: ${error.message}`);
}
}
async function fetchNodeChain() {
const node = document.getElementById('nodeChain').value;
try {
const response = await fetch(`http://${node}/chain`);
const data = await response.json();
const nodeChainDiv = document.getElementById('node-chain');
nodeChainDiv.innerHTML = '<h2>节点区块链</h2>';
if (data.error) {
alert(data.error);
} else {
data.chain.forEach(block => {
const blockDiv = document.createElement('div');
blockDiv.className = 'block';
blockDiv.innerHTML = `
<p><strong>索引:</strong> ${block.index}</p>
<p><strong>时间戳:</strong> ${block.timestamp}</p>
<p><strong>交易:</strong> ${JSON.stringify(block.transactions)}</p>
<p><strong>工作量证明:</strong> ${block.proof}</p>
<p><strong>上一个区块哈希:</strong> ${block.previous_hash}</p>
`;
nodeChainDiv.appendChild(blockDiv);
});
}
} catch (error) {
alert(`无法连接到节点: ${error.message}`);
}
}
document.addEventListener('DOMContentLoaded', fetchChain);
</script>
</body>
</html>
-
运维工作说明
1 基本功能测试
1.1 启动服务
运行代码,启动我们的服务
1.2 链的具体内容
分别查看链的具体内容,可以看到,初始化一个 BlockChain 对象之后,将会创建一个 “创世区块”,此时,区块链中只有一个区块,这个区块中还没有任何一笔交易。
1.3 创建交易
调用接口,创建一笔交易,添加到下一个区块中。(这里我们只使用 5000 端口的节点测试)。
这里我们发送 Post 请求,将发送者地址,接收者地址,交易金额作为参数传入。由着3个参数构成一笔交易的具体内容。
这里为了方便辨认,我们发送者和接收者采用易于辨识的名字。
然后我们这里创建2笔交易,交易发送者的名字分别为 “孔孟群” 和 “智慧小孔”。
1.4 查看5000完整链
我们再次查看5000端口节点的完整链
但是我们发现,链的长度并没有发生改变;这是因为,我们只是进行了交易,但是这些交易并没有被打包成为一个新的区块,我们还要调用 BlockChain 的 new_block() 方法,将交易打包成为一个新的区块。
1.5 打包新的区块
将交易打包成一个新的区块,这里我们调用 new_block ,打包生成一个区块,这里我们依旧使用5000 端口的节点进行测试。简单介绍一个这个方法:
在打包形成一个新的区块之前,所有的交易都存储在 BlockChain 类的 current_transactions 属性里面,我们只需要将这个属性作为 block 属性的一个子属性即可,然后再重置一下 current_transactions 的值即可,方便进行一下次交易。
可以看到,在第2个区块中,除了我们手动添加的2笔交易,发现还有一笔交易。实际上,这笔交易是我们在代码中手动添加的,这个 sender 为 0 表示这个结点已经挖掘出新的星币,然后这笔交易就是为了奖励那个找到工作量证明的那个用户。
1.6 查看整条链的内容
打包生成一个区块后,我们的链理论上应该是有2个区块了。我们调用接口来查看一下我们的链的具体内容。
可以发现,我们的链的长度为2,的确是有2个区块,验证了我的猜测,代码没有问题。
2 分布式功能测试
经过上面的测试过程,我们单一节点的区块链需要具备的功能基本完成了,但是区块链是一个分布式的系统,所以我们还需要进行分布式测试。
2.1 启动2个服务
我们在本机上使用不同的端口模拟不同的节点,第1个结点我们部署在 5000 端口,第2个结点我们部署在 5001 端口。
l5000端口的节点
l5001端口的节点
2.2 两条链的具体内容
分别查看两条链的具体内容。
l5000端口节点
l5001端口节点
可以看到,2条链都初始化成功,各自具有一个创世区块。
2.3 将5001端口节点注册到5000端口节点中去
比特币是分布式的,这里我们为了模仿比特币,启动多个不同端口的节点来模拟分布式。
同时,我们还需要让一个节点知道其相邻结点的存在,即分布式系统中的每一个节点,都需要存储该系统中的其他节点的记录。
这里我们通过 BlockChain 类的 register_node 方法来实现识别相邻节点的功能要求。
register_node 方法需要我们传入一个节点的地址,然后将这个地址添加到 BlockChain 类的 nodes 属性中,这个 nodes 是一个 set 集合。
可以看到,这里我们调用 5000 端口节点的 register_node() 方法,将 5001 端口的地址传入,返回结果显示新的结点已经被创建,说明我们的 5000 端口的节点已经知道 5001 端口节点的存在了。
2.4 测试共识机制
因为之前我们已经将 5001 端口的节点注册到了 5000 端口的节点中,所以 5000 端口的结点时已经知道了 5001 端口节点的存在的,现在只要在 5001 端口的节点上挖掘一些新的区块,使得 5001 端口节点的链的长度要比 5000 端口节点链的长度更长,然后在调用 5000 端口结点的 resolve_conflicts() 函数,如果发现 5000 端口的链被替换成立 5001 端口的链,这说明我们的共识算法是有效的。
好,下面开始我们的测试。
l我们首先在 5001 端口多打包生成一些区块
挖掘区块,使得5001端口有8个区块,即链的长度是8。
l接下来,调用5000端口节点的resolve_conflicts()方法,解决节点间的冲突
调用之前,我们的 5000 端口链的具体内容是这样的,只有1个区块:
l调用 resolve_conflicts() 方法
调用之后显示我们的链已经被替换了
l再次查看 5000 端口链的详细内容
发现链的长度变为了8,验证了我们刚才的猜想,说明我的共识算法是有效的。