肖臻老师《区块链技术与应用》笔记太干了?我用Python+Flask手搓了一个简易BTC交易模拟器
用Python+Flask构建比特币交易模拟器:从理论到实践的区块链学习之旅
当我在电脑屏幕上第N次回放肖臻老师讲解UTXO模型的片段时,突然意识到自己正陷入典型的技术学习困境——那些精妙的理论在脑海中盘旋,却找不到落地的支点。作为开发者,我们最有效的学习方式从来不是被动接受概念,而是通过亲手构建来解构复杂系统。这就是为什么我决定用200行Python代码,打造一个可以放在浏览器里把玩的比特币交易模拟器。
1. 环境搭建与项目架构设计
在开始编码之前,我们需要建立一个清晰的开发环境。这个模拟器将采用前后端分离的架构:后端使用Flask处理区块链逻辑,前端用简单的HTML/JavaScript实现可视化交互。这种设计不仅符合现代Web开发趋势,更能让我们专注于核心业务逻辑。
首先创建项目目录结构:
/btc-simulator
│── app.py # Flask主程序
│── blockchain.py # 核心区块链逻辑
│── static/
│ └── style.css # 前端样式
└── templates/
└── index.html # 前端界面
安装必要的Python包:
pip install flask flask-socketio cryptography
这里有几个关键的技术选型决策:
- Flask:轻量级Web框架,比Django更适合小型教学项目
- cryptography:用于处理椭圆曲线数字签名(ECDSA),这是比特币签名验证的基础
- SocketIO:实现实时前端更新,模拟区块链网络广播
提示:建议使用Python 3.8+版本以获得最佳兼容性。如果遇到依赖冲突,可以尝试创建虚拟环境。
2. 区块链核心数据结构实现
比特币的精妙之处首先体现在其数据结构设计上。我们需要实现三个核心类:交易(Transaction)、区块(Block)和区块链(Blockchain)。这些类将构成我们模拟器的基础骨架。
2.1 交易模型构建
在blockchain.py中,我们首先定义交易类:
class Transaction:
def __init__(self, sender, recipient, amount, inputs=None, signature=None):
self.sender = sender # 发送者公钥
self.recipient = recipient # 接收者地址
self.amount = amount # 转账金额
self.inputs = inputs or [] # 引用的UTXO
self.signature = signature # 数字签名
def to_dict(self):
return {
'sender': self.sender,
'recipient': self.recipient,
'amount': self.amount,
'inputs': [input.to_dict() for input in self.inputs]
}
def sign(self, private_key):
"""使用私钥对交易进行签名"""
signing_key = SigningKey.from_string(bytes.fromhex(private_key), curve=SECP256k1)
self.signature = signing_key.sign(json.dumps(self.to_dict()).encode()).hex()
关键点解析:
- 每个交易需要明确指定资金来源(inputs)和去向(recipient)
- 签名过程使用比特币相同的secp256k1椭圆曲线
- UTXO(未花费交易输出)模型确保不会发生双花攻击
2.2 区块与区块链实现
接下来是区块结构的Python实现:
class Block:
def __init__(self, index, transactions, previous_hash):
self.index = index
self.timestamp = time.time()
self.transactions = transactions
self.previous_hash = previous_hash
self.nonce = 0
self.hash = self.calculate_hash()
def calculate_hash(self):
block_string = json.dumps({
"index": self.index,
"timestamp": self.timestamp,
"transactions": [tx.to_dict() for tx in self.transactions],
"previous_hash": self.previous_hash,
"nonce": self.nonce
}, sort_keys=True).encode()
return hashlib.sha256(block_string).hexdigest()
区块链类维护整个链的状态:
class Blockchain:
def __init__(self):
self.chain = [self.create_genesis_block()]
self.pending_transactions = []
self.utxo = {} # 未花费交易输出集
self.difficulty = 4 # 挖矿难度(前导零个数)
def create_genesis_block(self):
"""创建创世区块"""
genesis_block = Block(0, [], "0")
# 预分配一些初始资金
genesis_transaction = Transaction(
"0",
"04dca23d...", # 预设的初始地址
1000,
[]
)
genesis_block.transactions.append(genesis_transaction)
return genesis_block
3. 关键算法实现与验证
有了基础数据结构后,我们需要实现比特币的几个核心算法:工作量证明、交易验证和UTXO管理。这些算法保证了比特币网络的安全运转。
3.1 工作量证明(PoW)实现
在Block类中添加挖矿方法:
def mine_block(self, difficulty):
while self.hash[:difficulty] != "0"*difficulty:
self.nonce += 1
self.hash = self.calculate_hash()
print(f"Block mined: {self.hash}")
对应的区块链添加新区块逻辑:
def add_block(self, block):
# 验证区块哈希是否符合难度要求
if block.hash[:self.difficulty] != "0"*self.difficulty:
return False
# 验证所有交易
for tx in block.transactions:
if not self.validate_transaction(tx):
return False
self.chain.append(block)
return True
3.2 交易验证逻辑
交易验证是比特币网络安全的基石:
def validate_transaction(self, transaction):
# 验证签名
try:
verifying_key = VerifyingKey.from_string(
bytes.fromhex(transaction.sender),
curve=SECP256k1
)
verifying_key.verify(
bytes.fromhex(transaction.signature),
json.dumps(transaction.to_dict()).encode()
)
except:
return False
# 验证输入UTXO是否存在且未被花费
input_sum = 0
for input_tx in transaction.inputs:
if input_tx.tx_id not in self.utxo:
return False
input_sum += input_tx.amount
# 验证输入总额 ≥ 输出金额
if input_sum < transaction.amount:
return False
return True
3.3 UTXO管理
UTXO(未花费交易输出)是比特币的核心设计:
def update_utxo(self, block):
for tx in block.transactions:
# 移除已花费的UTXO
for input_tx in tx.inputs:
if input_tx.tx_id in self.utxo:
del self.utxo[input_tx.tx_id]
# 添加新的UTXO
tx_id = hashlib.sha256(json.dumps(tx.to_dict()).encode()).hexdigest()
self.utxo[tx_id] = UTXO(
tx_id=tx_id,
recipient=tx.recipient,
amount=tx.amount
)
4. Web接口与可视化实现
最后,我们需要通过Flask将这些功能暴露为Web接口,并创建一个简单的前端界面来交互。
4.1 Flask API设计
在app.py中设置路由:
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
values = request.get_json()
required = ['sender', 'recipient', 'amount', 'private_key']
if not all(k in values for k in required):
return 'Missing values', 400
# 创建交易
tx = Transaction(
values['sender'],
values['recipient'],
values['amount']
)
tx.sign(values['private_key'])
blockchain.add_pending_transaction(tx)
return "Transaction added", 201
@app.route('/mine', methods=['GET'])
def mine():
last_block = blockchain.last_block
new_block = Block(
index=last_block.index + 1,
transactions=blockchain.pending_transactions,
previous_hash=last_block.hash
)
new_block.mine_block(blockchain.difficulty)
blockchain.add_block(new_block)
return json.dumps(new_block.__dict__), 200
4.2 前端交互界面
在templates/index.html中创建简单UI:
<div class="container">
<h2>创建交易</h2>
<form id="transaction-form">
<input type="text" id="sender" placeholder="发送者地址">
<input type="text" id="recipient" placeholder="接收者地址">
<input type="number" id="amount" placeholder="金额">
<input type="text" id="private-key" placeholder="私钥">
<button type="submit">发送交易</button>
</form>
<h2>区块链状态</h2>
<div id="blockchain-view"></div>
<button id="mine-button">开始挖矿</button>
</div>
添加JavaScript交互逻辑:
document.getElementById('transaction-form').onsubmit = async (e) => {
e.preventDefault();
const response = await fetch('/transactions/new', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sender: document.getElementById('sender').value,
recipient: document.getElementById('recipient').value,
amount: document.getElementById('amount').value,
private_key: document.getElementById('private-key').value
})
});
alert(await response.text());
};
5. 运行与调试技巧
完成所有代码后,通过以下命令启动模拟器:
python app.py
访问http://localhost:5000即可开始实验。这里有几个实用的调试技巧:
-
查看区块链状态: 访问
http://localhost:5000/chain获取完整区块链数据 -
生成测试密钥对:
from cryptography.hazmat.primitives.asymmetric import ec private_key = ec.generate_private_key(ec.SECP256k1()) public_key = private_key.public_key().public_bytes( encoding=serialization.Encoding.X962, format=serialization.PublicFormat.UncompressedPoint ).hex() -
调整挖矿难度: 修改
blockchain.py中的difficulty值,体验不同难度下的挖矿速度差异
在开发过程中,我遇到最有趣的问题是UTXO集的同步问题。最初实现时忘记在新区块被接受后更新UTXO集,导致系统出现了"双花"漏洞——这正是比特币设计要解决的核心问题。这个bug让我深刻理解了UTXO模型的实际价值。
更多推荐



所有评论(0)