Releases: Zeratal/BlockChain
v0.0.7_3
v0.0.7_2: 添加UTXO更新功能
增添UTXO的同步和共识机制
v0.0.7_1
v0.0.7_0
交易池和UTXO(0.0.6)
交易池和UTXO(0.0.6)
我来帮您实现交易池(Transaction Pool)和UTXO(Unspent Transaction Output)模型。这是区块链系统中非常重要的两个组件。
主要的变更:
- UTXO模型:
- 创建了
UTXO类来表示未花费的交易输出 - 实现了
UTXOPool类来管理所有UTXO - 提供了查询余额、选择UTXO等功能
- 交易池:
- 创建了
TransactionPool类来管理待处理的交易 - 实现了交易的验证、添加、移除等功能
- 使用互斥锁保证线程安全
- 交易结构更新:
- 添加了
TransactionInput和TransactionOutput类 - 更新了
Transaction类以支持UTXO模型 - 实现了更安全的交易验证机制
- 区块链更新:
- 集成了UTXO池和交易池
- 更新了区块添加逻辑,支持UTXO模型
- 实现了余额查询的新方法
主要改进:
- 更好的安全性:UTXO模型可以防止双重支付
- 更高的效率:交易池可以缓存待处理交易
- 更准确的余额计算:基于UTXO而不是简单的加减
- 更好的并发支持:使用互斥锁保护共享资源
使用示例:
// 创建交易
Transaction tx(fromAddress, toAddress, amount);
// 选择UTXO
auto utxos = utxoPool.selectUTXOs(fromAddress, amount);
// 添加输入
for (const auto& utxo : utxos) {
tx.addInput(TransactionInput(utxo.getTxId(), utxo.getOutputIndex(), signature));
}
// 添加输出
tx.addOutput(TransactionOutput(amount, toAddress));
if (totalInput > amount) {
tx.addOutput(TransactionOutput(totalInput - amount, fromAddress)); // 找零
}
// 添加到交易池
blockchain.addTransactionToPool(tx);UTXO和交易池的概念和实现。
UTXO (Unspent Transaction Output) 模型
UTXO是比特币等区块链系统中使用的一种交易模型。它的核心思想是:
- 基本概念:
- 每个交易输出(UTXO)代表一定数量的加密货币
- 每个UTXO只能被使用一次
- 交易必须使用已有的UTXO作为输入,并创建新的UTXO作为输出
- UTXO类的实现:
class UTXO {
std::string txId_; // 交易ID
int outputIndex_; // 输出索引
double amount_; // 金额
std::string owner_; // 所有者地址
bool spent_; // 是否已花费
};- UTXOPool的功能:
- 管理所有未花费的交易输出
- 提供余额查询
- 选择UTXO进行交易
class UTXOPool {
// 存储结构:交易ID -> (输出索引 -> UTXO)
std::map<std::string, std::map<int, UTXO>> utxos_;
// 主要方法
void addUTXO(const UTXO& utxo); // 添加UTXO
void removeUTXO(const std::string& txId, int outputIndex); // 移除UTXO
double getBalance(const std::string& address); // 查询余额
std::vector<UTXO> selectUTXOs(const std::string& address, double amount); // 选择UTXO
};交易池 (Transaction Pool)
交易池是一个临时存储待处理交易的容器,主要功能包括:
- 基本概念:
- 存储等待被打包进区块的交易
- 提供交易验证功能
- 管理交易的添加和移除
- TransactionPool的实现:
class TransactionPool {
std::map<std::string, Transaction> transactions_; // 存储交易
mutable std::mutex mutex_; // 线程安全锁
// 主要方法
bool addTransaction(const Transaction& transaction, const UTXOPool& utxoPool);
void removeTransaction(const std::string& txId);
std::vector<Transaction> getTransactions() const;
};- 交易验证流程:
bool TransactionPool::isValidTransaction(const Transaction& transaction, const UTXOPool& utxoPool) const {
// 验证签名
if (!transaction.verifySignature()) return false;
// 验证余额
if (!utxoPool.hasEnoughFunds(transaction.getFrom(), transaction.getAmount())) return false;
// 验证金额
if (transaction.getAmount() <= 0) return false;
// 验证发送者和接收者
if (transaction.getFrom() == transaction.getTo()) return false;
return true;
}工作流程
- 创建交易:
// 1. 选择UTXO
auto utxos = utxoPool.selectUTXOs(fromAddress, amount);
// 2. 创建交易输入
for (const auto& utxo : utxos) {
tx.addInput(TransactionInput(utxo.getTxId(), utxo.getOutputIndex(), signature));
}
// 3. 创建交易输出
tx.addOutput(TransactionOutput(amount, toAddress));
// 如果有找零
if (totalInput > amount) {
tx.addOutput(TransactionOutput(totalInput - amount, fromAddress));
}- 交易处理:
// 1. 添加到交易池
blockchain.addTransactionToPool(tx);
// 2. 挖矿时从交易池获取交易
auto pendingTransactions = transactionPool.getTransactions();
// 3. 交易确认后更新UTXO池
void Blockchain::updateUTXOPool(const Block& block) {
for (const auto& tx : block.getTransactions()) {
// 移除已使用的UTXO
for (const auto& input : tx.getInputs()) {
utxoPool_.removeUTXO(input.getTxId(), input.getOutputIndex());
}
// 添加新的UTXO
for (size_t i = 0; i < tx.getOutputs().size(); ++i) {
const auto& output = tx.getOutputs()[i];
UTXO utxo(tx.getTransactionId(), i, output.getAmount(), output.getOwner());
utxoPool_.addUTXO(utxo);
}
}
}这种设计的优点:
- 防止双重支付
- 提高交易验证效率
- 支持并行处理
- 更好的隐私保护
- 更准确的余额计算
主程序更新:
- 交易创建流程:
-
现在每个交易都需要选择UTXO作为输入
-
添加了交易输入和输出的处理
-
实现了找零机制
- 交易池集成:
-
交易先添加到交易池
-
挖矿时从交易池获取待处理交易
-
交易确认后从交易池中移除
- 余额查询:
-
使用UTXO池来查询余额
-
不再使用钱包的余额缓存
- 输出信息:
-
添加了交易输入和输出的详细信息
-
显示UTXO的使用情况
-
显示找零信息
- 本地化:
-
将所有输出信息改为中文
-
优化了输出格式
主要改进:
-
更安全的交易机制
-
更准确的余额计算
-
更好的交易追踪
-
更清晰的输出信息
阶段总结
本阶段用例在链上添加了2个数据
- 交易池
- UTXO池
事实上,UTXO在链上是永久驻留,其记录了每笔交易的结果(财富属于谁)
- 每次交易发生,该次交易的发起方的历史UTXO都被清除,并生成一个新的代表余额的UTXO;
- 每次交易发生,该次交易的接收方将添加一个新的UTXO(所以某个钱包如果只接收不支出,则UTXO会越来越多
而Transaction Pool只是缓存未完成交易
- 当链上新增一个块时,会检查链上未完成交易,并将所有未完成交易计入此块的交易。
- 对该块挖矿(工作量证明)
- 针对该块更新UTXO(即将交易池中未完成交易完成)
- 清空交易池
所以在此例中,交易池不是必须的。
示例程序流程
-
创建三个钱包(构造时生成秘钥对),获取钱包公钥作为身份识别
-
创建链,生成一个0块。id为0,空交易,前置块Hash为0
-
链上注册钱包,公钥到钱包的映射
-
创建三个系统交易(初始交易,准备给钱包充值)
- 初始化交易(系统,钱包ID,金额)
- 系统交易特殊签名
- 给交易插入一个“交易输出”(金额,钱包ID)
- 更新交易Hash值(每次给交易添加输入输出都要更新Hash)
-
将以上交易列表加入链(生成块)
- 合并新交易和待处理交易(==transactionPool_==无)
- ==生成块==
- 初始化块index,交易列表,前块的hash值
- 获取时间戳
- 创建MerkleTress生成merkleRoot_
- 计算hash值
- ==挖矿(更新hash值)==
- ==块添加到链上==
- ==更新UTXO池==(链操作,入参是块)
- 遍历交易
- 遍历交易输入(这里无)
- 遍历交易输出(这里3个)
- 生成一个UTXO(交易ID,序号(0),金额,拥有者ID)
- UTXO加入==utxoPool_==(chain上总池)
- 第一级map key为交易ID
- 第二级map key为utxo的输出index(这里永远为0吧?)
- 遍历交易
- 清空交易池中已确认的交易
- 遍历之前合并的交易(该交易被加入块)
- ==transactionPool_== 中删除所以交易(这里transactionPool_为空,所以无操作)
- 遍历之前合并的交易(该交易被加入块)
-
检查初始余额
- 通过链操作获取各钱包余额。
- ==utxoPool_==中获取余额
- 遍历2层map,找到所有者地址和请求钱包地址一致的utxo,累计金额
- ==utxoPool_==中获取余额
- 通过链操作获取各钱包余额。
-
==开始正常交易==
-
==创建交易1==(Alice向Bob转10块,注意,这里没有创建交易输入和输出)
-
使用alice的钱包对此次交易签名,==注,此签名没有写入交易==(果然需要写入)
-
通过链操作获取Alice拥有的UTXO
-
遍历Alice所有UTXO,并构造“交易输入”加入此次交易
- utxo的交易ID(此utxo生成时的交易)
- utxo的输出index(感觉无用的字段,其总是为0)
- 交易签名写入此“交易输入”
-
构造向Bob的“交易输出”写入交易
-
通过链操作获取Alice的余额,并判断是否满足本次交易,如果满足
- 计算本交易如果成功,则Alice余额是多少
- 构造一个向Alice写入上述余额的“交易输出”
-
==创建交易2==(Bob向Charlie转5块,注意,这里没有创建交易输入和输出)
-
使用Bob的钱包对此次交易签名,==注,此签名没有写入交易==(果然需要写入)
-
通过链操作获取Bob拥有的UTXO
-
遍历Bob所有UTXO,并构造==“交易输入”==加入此次交易
- utxo的交易ID(此utxo生成时的交易)
- utxo的输出index(感觉无用的字段,其总是为0)
- 交易签名写入此“交易输入”
-
构造向Charlie的==“交易输出”==写入交易
-
通过链操作获取Bob的余额,并判断是否满足本次交易,如果满足
- 计算本交易如果成功,则Bob余额是多少
- 构造一个向Bob写入上述余额的==“交易输出”==
-
==创建交易==3(Charlie向Alice转2.5块,注意,这里没有创建交易输入和输出)
-
使用Charlie的钱包对此次交易签名,==注,此签名没有写入交易==(果然需要写入)
-
通过链操作获取Charlie拥有的UTXO
-
遍历Charlie所有UTXO,并构造“交易输入”加入此次交易(==交易的输入是发起者所有的UTXO==)
- utxo的交易ID(此utxo生成时的交易)
- utxo的输出index(感觉无用的字段,其总是为0)
- 交易签名写入此“交易输入”
-
构造向Alice的“交易输出”写入交易(==接受者的交易输出==)
-
通过链操作获取Charlie的余额,并判断是否满足本次交易,如果满足
- 计算本交易如果成功,则Charlie余额是多少
- 构造一个向Charlie写入上述余额的“交易输出”(==发起者的交易输出==,这里其实是统计发起者的余额)
-
==通过链操作将交易加入交易池(transactionPool_)==
- 在transactions_中查找交易,如果找到返回失败
- 验证交易
- 验证签名(前文提到,交易签名要写入交易)
- 验证发送者有足够余额(这里利用入参传进来的utxo池)
- 验证交易金额大于0
- 验证交易双方不是一个地址
- 添加到交易池
-
往链上增加一个块(无需携带新的交易,链会检查之前加入的未完成交易)
- 合并新交易和待处理交易(==transactionPool_==有,transactions无)
- ==生成块==
- 初始化块index,交易列表,前块的hash值
- 获取时间戳
- 创建MerkleTress生成merkleRoot_
- 计算hash值
- ==挖矿(更新hash值)==
- ==块添加到链上==
- ==更新UTXO池==(链操作,入参是块)
- 遍历交易
- 遍历交易输入
- 如前文(这里包括了发起者所有的UTXO,==通过向链上的UTXO系统获取==,即它可能有多个),此次遍历将在链上删除所有这些UTXO,即发起方链上原有的所有UTXO将被删除(下文会计算并重构一个余额UTXO)
2025.05.20 2:21 不行了,先睡了,不然明天就废了- 遍历交易输出(这里2个,一个是本次交易的接收方新增一个UTXO,一个是本次交易发送方重新构建一个余额UTXO)
- 生成一个UTXO(交易ID,序号(本次交易的输出序号,这里可以是0和1),金额,拥有者ID)
- UTXO加入==utxoPool_==(chain上总池)
- 第一级map key为交易ID
- 第二级map key为utxo的输出index(这里永远为0吧?,这里明显可以为1)
- 遍历交易输入
- 遍历交易
- 清空交易池中已确认的交易
- 遍历之前合并的交易(该交易被加入块)
- ==transactionPool_== 中删除所以交易(这里transactionPool_为空,所以无操作)
- 遍历之前合并的交易(该交易被加入块)
-
通过链上操作获取各个钱包的余额并显示
-
==开始第二轮交易==
-
交易4:Alice sends 7.5 coins to Charlie(生成一个交易,链上获取Alice UTXO,用Alice钱包给交易签名,将Alice所有UTXO加入交易输入,给Charlie添加交易输出,给Alice添加交易余额)
-
交易5:Charlie sends 3 coins to Bob(生成一个交易,链上获取Charlie UTXO,用Alice钱包给交易签名,将Charlie 所有UTXO加入交易输入,给Bob添加交易输出,给Charlie 添加交易余额)
-
将交易4和5加入链上未处理交易(==验证交易,通过才允许加入==)
-
在链上添加一个块(生成块<构造成员,生成merkleroot,计算hash>,挖矿,添加到链上,更新UTXO池<入参是块>,清空交易池中已确认交易)
-
通过链上操作获取各个钱包的余额并显示
-
打印链信息
v0.0.4
添加钱包系统,包括密钥生成、签名和验证功能。
Full Changelog: v0.0.2...v0.0.4
v0.0.2
简单的交易结构和Merkle树
Full Changelog: v0.0.1...v0.0.2
v0.0.1
Basic blockchain demo code, including initializing the chain, adding blocks to the chain, and verification.
Full Changelog: https://github.com/Zeratal/BlockChain/commits/v0.0.1