diff --git a/BTC/Advanced/Lighting Network/README.md b/BTC/Advanced/Lighting Network/README.md new file mode 100644 index 000000000..01af411ce --- /dev/null +++ b/BTC/Advanced/Lighting Network/README.md @@ -0,0 +1,40 @@ +**闪电网络**(Lightning Network)是一种比特币的第二层支付协议,旨在通过减少链上交易数量,实现快速、低成本的交易。这一网络在比特币区块链之上构建,旨在解决比特币当前的扩展性和交易速度问题。以下是对闪电网络的核心概念及其功能的详细介绍: + +### 1. **核心概念:支付通道** + +闪电网络的基本运行机制是通过支付通道。具体操作如下: + - **创建支付通道**:两方需要频繁交易时,可以通过创建支付通道来进行。这需要双方向一个多签名钱包存入初始资金,并记录在区块链上作为一次链上交易。 + - **链下交易**:通道建立后,双方可以直接交换签名的余额更新,这些更新是链下进行的,不需要经过全网确认,从而大大提高了交易速度。 + - **关闭通道**:当两方不再需要继续交易时,可以选择关闭通道。此时通道的最终余额状态会作为一次链上交易记录在比特币区块链上。这样可以极大减少区块链的负担,因为多笔交易只需记录两次链上交易(开通和关闭)。 + +### 2. **闪电网络中的交易机制** + +在闪电网络中,通道内的交易是即时确认的。具体过程如下: + - **增量余额更新**:每次链下交易只更新支付通道中的余额状态,而不广播至比特币区块链。 + - **去中心化的信任交易**:每次状态更新都由双方签名确认,确保任何一方都无法篡改交易余额,避免了一方试图多拿资金的可能。 + +### 3. **跨通道支付的路由** + +闪电网络不仅限于两个通道用户间的直接支付,还支持多跳支付,使得资金能够通过多个通道转账给目标用户。具体过程如下: + - **路径查找**:如果两个用户之间没有直接的通道,网络会寻找合适的路径通过其他节点完成支付。 + - **支付的原子性**:只有在路径中所有中间节点同意并完成自己的部分支付时,整个支付才会完成,确保资金在多跳过程中不被截留。 + +### 4. **主要优势** + + - **扩展性**:通过链下交易的方式,闪电网络大幅减少了比特币区块链的负载,理论上可以支持每秒数百万次交易。 + - **降低费用**:大部分交易是链下进行的,交易费用很低,仅需链上交易费用的一小部分。 + - **即时支付**:闪电网络的交易几乎是即时完成的,使其适用于日常消费,而不像链上交易那样可能需要数分钟。 + +### 5. **挑战与局限性** + + - **流动性要求**:为了确保大额交易畅通,路径中的每个节点必须在通道中拥有足够的资金,否则可能会阻塞交易。 + - **通道管理**:用户需要手动开通和关闭通道,每次操作都需支付链上费用,因此频繁操作的成本较高。 + - **安全风险**:虽然闪电网络安全性较高,但它并非没有风险。节点需要保持在线以防止攻击者试图广播过时的交易。 + +### 6. **闪电网络的应用场景及未来** + + - **小额支付**:由于费用低廉,闪电网络非常适合进行小额支付,如内容打赏、小额捐赠和应用内购买等。 + - **商户和零售**:交易速度接近实时,闪电网络能使比特币成为零售、餐饮等日常消费的理想支付方式。 + - **跨境支付**:它能快速且低成本地实现国际汇款,为跨境支付提供了一种便利的途径。 + +闪电网络随着开发和用户的持续增长在不断发展。它为解决比特币的扩展性提供了一个极具前景的方案,但要充分发挥其潜力,还需要一个可靠且强大的节点网络支持。 \ No newline at end of file diff --git a/BTC/Advanced/Lighting Network/config/index.md b/BTC/Advanced/Lighting Network/config/index.md new file mode 100644 index 000000000..ed0a9da71 --- /dev/null +++ b/BTC/Advanced/Lighting Network/config/index.md @@ -0,0 +1,106 @@ +要在比特币节点上配置和运行闪电网络节点,需要执行以下步骤。配置过程涉及安装比特币全节点、下载闪电网络节点软件(如 `lnd` 或 `c-lightning`),并设置基本的连接和通道参数。以下是详细步骤: + +### 步骤 1:安装比特币全节点 + +1. **下载比特币核心(Bitcoin Core)**: + - 在 [比特币官网](https://bitcoin.org/en/download)下载最新版 Bitcoin Core 软件,并按照官方文档安装。 + +2. **启动比特币节点**: + - 使用以下命令启动比特币节点: + ```bash + bitcoind -daemon + ``` + - 节点需要同步区块链数据,建议启用 `pruned` 模式,以节省存储空间。 + +3. **修改 `bitcoin.conf` 配置文件**: + - 打开 `bitcoin.conf` 文件(通常位于 `~/.bitcoin/bitcoin.conf`)并添加以下内容以支持闪电网络: + ```ini + server=1 + txindex=1 + rpcuser=your_rpc_user + rpcpassword=your_rpc_password + ``` + +### 步骤 2:选择并安装闪电网络节点软件 + +目前,主流的闪电网络实现有 `lnd`(Lightning Labs)、`c-lightning`(Blockstream)和 `eclair`(ACINQ)。以 `lnd` 为例: + +1. **下载并安装 `lnd`**: + - 可以在 [GitHub](https://github.com/lightningnetwork/lnd) 下载最新版本的 `lnd`。 + - 安装完 `lnd` 后,使用以下命令启动 `lnd` 服务: + ```bash + lnd + ``` + +2. **配置 `lnd.conf` 文件**: + - 在 `~/.lnd/lnd.conf` 文件中设置基本配置,连接到比特币节点: + ```ini + [Application Options] + alias=YourNodeAlias # 设置节点的别名 + color=#FF0000 # 设置节点颜色(十六进制) + + [Bitcoin] + bitcoin.active=1 + bitcoin.mainnet=1 # 或 bitcoin.testnet=1 用于测试网 + bitcoin.node=bitcoind + + [Bitcoind] + bitcoind.rpcuser=your_rpc_user + bitcoind.rpcpass=your_rpc_password + bitcoind.rpchost=localhost + bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332 + bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333 + ``` + +### 步骤 3:启动并初始化闪电网络节点 + +1. **初始化闪电网络节点**: + - 使用以下命令启动 `lnd` 并生成钱包: + ```bash + lncli create + ``` + - 你将被要求设置密码并备份助记词,这对于保护你的资金非常重要。 + +2. **启动和连接**: + - 启动 `lnd` 服务后,可以使用 `lncli` 命令管理和查询节点状态。例如: + ```bash + lncli getinfo + ``` + +### 步骤 4:建立闪电网络支付通道 + +1. **获取资金**: + - 首先为节点充值比特币。可以使用 `lncli newaddress` 获取节点地址,然后从外部钱包转账。 + +2. **打开通道**: + - 使用以下命令来打开通道: + ```bash + lncli openchannel --node_key=对方节点公钥 --local_amt=通道资金 + ``` + - 其中 `node_key` 是对方节点的公钥,`local_amt` 是设置的通道资金数量。 + +3. **监控通道状态**: + - 打开通道后,可以使用 `lncli listchannels` 查看通道状态,并确保通道正常运行。 + +### 步骤 5:管理和使用闪电网络节点 + +1. **发送和接收支付**: + - 可以通过 `lncli` 发起支付,或生成支付请求让他人向你支付。 + - 例如,生成收款请求: + ```bash + lncli addinvoice --amt=金额 + ``` + - 通过支付请求二维码或字符串,其他用户可以通过他们的闪电节点完成支付。 + +2. **关闭通道**: + - 如果不再需要通道,可以通过以下命令手动关闭: + ```bash + lncli closechannel --funding_txid=交易ID --output_index=输出索引 + ``` + +3. **备份和恢复**: + - 为了防止意外情况,定期备份数据至安全位置。`lnd` 支持通道备份,可以使用 `lncli exportchanbackup` 导出备份文件。 + +### 小结 + +通过以上步骤,您可以成功配置并运行闪电网络节点。闪电网络通过支付通道的机制,降低比特币链上负载,实现了低成本、快速的小额支付。 \ No newline at end of file diff --git a/BTC/Advanced/Lighting Network/config/index.sh b/BTC/Advanced/Lighting Network/config/index.sh new file mode 100644 index 000000000..009fc15ba --- /dev/null +++ b/BTC/Advanced/Lighting Network/config/index.sh @@ -0,0 +1,79 @@ + +### 设置并启动闪电网络节点的 Shell 脚本 + +```bash +#!/bin/bash + +# 设置变量 +BITCOIN_USER="your_rpc_user" # 替换为你的比特币 RPC 用户名 +BITCOIN_PASSWORD="your_rpc_password" # 替换为你的比特币 RPC 密码 +BITCOIN_CONF_PATH="$HOME/.bitcoin/bitcoin.conf" +LND_CONF_PATH="$HOME/.lnd/lnd.conf" + +# 步骤 1:安装必要的依赖项 +echo "Updating system and installing dependencies..." +sudo apt-get update -y && sudo apt-get upgrade -y +sudo apt-get install -y wget jq unzip + +# 步骤 2:下载并安装 Bitcoin Core +echo "Downloading and installing Bitcoin Core..." +wget https://bitcoin.org/bin/bitcoin-core-24.0.1/bitcoin-24.0.1-x86_64-linux-gnu.tar.gz +tar -xzf bitcoin-24.0.1-x86_64-linux-gnu.tar.gz +sudo install -m 0755 -o root -t /usr/local/bin bitcoin-24.0.1/bin/* + +# 步骤 3:配置比特币节点 +echo "Configuring Bitcoin node..." +mkdir -p "$HOME/.bitcoin" +cat < "$BITCOIN_CONF_PATH" +server=1 +txindex=1 +prune=600 # 如果存储空间有限 +rpcuser=$BITCOIN_USER +rpcpassword=$BITCOIN_PASSWORD +EOF + +# 启动比特币节点 +echo "Starting Bitcoin node..." +bitcoind -daemon +sleep 10 # 等待节点启动 + +# 步骤 4:下载并安装 lnd +echo "Downloading and installing lnd..." +wget https://github.com/lightningnetwork/lnd/releases/download/v0.15.0-beta/lnd-linux-amd64-v0.15.0-beta.tar.gz +tar -xzf lnd-linux-amd64-v0.15.0-beta.tar.gz +sudo install -m 0755 -o root -t /usr/local/bin lnd-linux-amd64-*/lnd +sudo install -m 0755 -o root -t /usr/local/bin lnd-linux-amd64-*/lncli + +# 步骤 5:配置 lnd 节点 +echo "Configuring lnd node..." +mkdir -p "$HOME/.lnd" +cat < "$LND_CONF_PATH" +[Application Options] +alias=YourNodeAlias # 设置节点别名 +color=#FF0000 # 设置节点颜色 + +[Bitcoin] +bitcoin.active=1 +bitcoin.mainnet=1 +bitcoin.node=bitcoind + +[Bitcoind] +bitcoind.rpcuser=$BITCOIN_USER +bitcoind.rpcpass=$BITCOIN_PASSWORD +bitcoind.rpchost=localhost +bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332 +bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333 +EOF + +# 步骤 6:启动 lnd 节点 +echo "Starting lnd node..." +lnd --configfile="$LND_CONF_PATH" & + +# 步骤 7:等待 lnd 启动后初始化钱包 +sleep 15 # 等待 lnd 完全启动 +echo "Creating lnd wallet..." +echo "Please follow the instructions to create a wallet for your lnd node:" +lncli create + +echo "Lightning Network node setup is complete!" +``` \ No newline at end of file diff --git a/BTC/Advanced/Lighting Network/technical/imgs/offchain.png b/BTC/Advanced/Lighting Network/technical/imgs/offchain.png new file mode 100644 index 000000000..0061356df Binary files /dev/null and b/BTC/Advanced/Lighting Network/technical/imgs/offchain.png differ diff --git a/BTC/Advanced/Lighting Network/technical/imgs/open.png b/BTC/Advanced/Lighting Network/technical/imgs/open.png new file mode 100644 index 000000000..2062ebcd7 Binary files /dev/null and b/BTC/Advanced/Lighting Network/technical/imgs/open.png differ diff --git a/BTC/Advanced/Lighting Network/technical/imgs/route.png b/BTC/Advanced/Lighting Network/technical/imgs/route.png new file mode 100644 index 000000000..7c2f110dd Binary files /dev/null and b/BTC/Advanced/Lighting Network/technical/imgs/route.png differ diff --git a/BTC/Advanced/Lighting Network/technical/imgs/transfer.png b/BTC/Advanced/Lighting Network/technical/imgs/transfer.png new file mode 100644 index 000000000..445ce7543 Binary files /dev/null and b/BTC/Advanced/Lighting Network/technical/imgs/transfer.png differ diff --git a/BTC/Advanced/Lighting Network/technical/index.md b/BTC/Advanced/Lighting Network/technical/index.md new file mode 100644 index 000000000..d3bca6900 --- /dev/null +++ b/BTC/Advanced/Lighting Network/technical/index.md @@ -0,0 +1,75 @@ +闪电网络(Lightning Network,LN)是一种基于比特币区块链的**第二层支付协议**,旨在解决比特币主网的可扩展性和交易效率问题。它通过引入**支付通道**和**离线交易机制**,极大地减少了对链上交易的依赖,从而提升交易速度和降低成本。以下是闪电网络的主要技术原理。 + + +### 1. **支付通道的概念** + +支付通道是指两个用户之间创建的**链下交易**路径,允许双方在通道内多次转账,而无需每笔交易都记录在主链上。只在开通和关闭通道时需要链上交易,以确保资金安全。这种方式被称为“链下交易”,通过支付通道,双方可以在不影响区块链的情况下快速、安全地互相转账。 + +- **开通通道**:用户 A 和用户 B 向一个多重签名地址存入比特币,该地址需要两方的签名才能支出资金。 +- **更新余额**:两方可以通过交换新的余额分配签名,来更新支付通道的余额,而不进行链上记录。 +- **关闭通道**:当用户希望关闭通道时,最后的余额分配会提交到链上,进行最终结算。 + + +![offchain transaction](./imgs/offchain.png) + +### 2. **哈希时间锁定合约(HTLC)** + +HTLC 是闪电网络中的关键技术,用于确保链下交易的安全性。它通过**哈希锁定和时间锁定**的组合,让资金只有在特定条件满足时才会被转移,从而防止一方在交易完成前恶意撤回资金。 + +- **哈希锁定**:支付者生成一个随机数,并将其哈希值发送给接收者。接收者只有在知道该哈希值的原始输入时,才能解锁资金。 +- **时间锁定**:如果接收者未在指定时间内解锁资金,支付者可以撤销交易。这防止了接收者长时间不领取资金的情况。 + +HTLC 使闪电网络能够跨越多个节点转账,从而形成**跨通道的支付路径**,实现了网络的互联。 + +### 3. **路由支付** + +在闪电网络中,支付不需要直接的支付通道。即使没有直接的链下通道,用户也可以通过多个中介节点实现转账。闪电网络中的节点将自动寻找**最优路径**来完成支付,这一过程被称为路由。 + +- **多跳支付**:通过多个节点传递支付,用户可以利用其他节点作为中转站发送资金。 +- **路径发现和路由选择**:闪电网络使用路由协议(如 Onion 路由)找到最佳路径,确保资金在最短路径内传输,并提高了交易隐私性。 + + +![route pay](./imgs/route.png) + +### 4. **即时交易与低费用** + +与比特币区块链的**确认时间**相比,闪电网络中的链下交易几乎是即时的,因为交易只需双方签名并在通道中达成一致。而且,闪电网络的交易费用通常较低,仅需支付通道的维护费用或微量的路由费用,这使得其适合处理**小额支付**和**高频支付**。 + +### 5. **去中心化网络结构** + +闪电网络的设计是去中心化的,节点自由加入和退出,且不依赖于单一服务器或第三方。用户既可以选择与其他节点建立通道,也可以选择关闭通道。每个节点都可以自主设置路由费用,以激励更多节点参与进来,从而扩大网络的支付能力和覆盖范围。 + +### 6. **链下结算和通道关闭** + +闪电网络中的所有链下交易都是基于承诺的状态更新协议。只有当通道关闭时,链下的最后交易状态才会记录在链上: + +- **双向承诺**:在链下交易的过程中,任何一方都可以选择关闭通道,最后的余额状态将提交到链上,并进行结算。 +- **惩罚机制**:如果一方试图在链上提交过期的余额分配,另一方可以提交最新的状态,没收对方的余额。这种机制保证了用户不会恶意提交无效交易。 + +### 7. **闪电网络的优点和挑战** + +**优点**: +- **扩展性**:通过链下交易和支付通道,大大减轻了区块链的负担。 +- **低费用**:只在开通、关闭通道时支付链上交易费用,其余链下交易几乎无费用。 +- **隐私性**:交易不直接记录在区块链上,增强了交易隐私性。 +- **实时交易**:链下交易几乎是即时完成的。 + +**挑战**: +- **通道流动性**:当通道内的资金耗尽时,需要重新注资或重新开通新通道。 +- **网络连通性**:尽管闪电网络允许通过多跳支付进行路由,但找到稳定的路径可能会变得复杂。 +- **脱机风险**:由于是链下交易,断开网络或节点意外退出可能会影响交易的正常进行。 + +### 示例:使用闪电网络支付流程 + +1. **Alice 和 Bob 开通支付通道**:Alice 和 Bob 创建一个多重签名地址,并将 5 BTC 存入其中。 +![open channel](./imgs/open.png) + +2. **支付流程**:Alice 想给 Charlie 支付 1 BTC。Alice 和 Charlie 没有直接的通道,但 Bob 与 Charlie 之间有一个通道。因此,Alice 可以通过 Bob 将这笔金额转给 Charlie。 +3. **HTLC 保护**:Alice 创建一个 HTLC,将 1 BTC 锁定到 Bob,要求 Charlie 提供随机数来解锁。Bob 通过 Charlie 完成支付。 +4. **支付完成**:Charlie 提供随机数,Bob 获得资金解锁权,并最终完成 Alice 到 Charlie 的支付。 +![transfer](./imgs/transfer.png) + + +### 总结 + +闪电网络通过支付通道和 HTLC 实现了离线链下交易,同时通过去中心化的路由机制保证了可扩展性和交易隐私。尽管其实现和配置稍显复杂,但闪电网络为比特币和其他区块链提供了一种有效的扩容解决方案,极大地提升了区块链的应用潜力。 \ No newline at end of file diff --git a/BTC/Advanced/Multisign/OP_CHECKMULTISIG/README.md b/BTC/Advanced/Multisign/OP_CHECKMULTISIG/README.md new file mode 100644 index 000000000..34f134ba9 --- /dev/null +++ b/BTC/Advanced/Multisign/OP_CHECKMULTISIG/README.md @@ -0,0 +1,245 @@ +## OP_CHECKMULTISIG + +`OP_CHECKMULTISIG` 是比特币脚本语言中的一个操作码(opcode),用于实现多重签名验证。在比特币交易中,`OP_CHECKMULTISIG` 允许指定多个公钥,并要求至少有一定数量(M)的签名来验证交易。此功能广泛应用于多重签名钱包和联合管理的比特币地址,旨在增强比特币网络的安全性和灵活性。 + +### 背景 + +1. **早期比特币设计**: + - 比特币最初设计主要关注点是点对点的单一用户交易。 + - 简单的签名机制足以支持基本的比特币转账功能。 + +2. **安全需求增加**: + - 随着比特币的普及,越来越多的用户和组织开始使用比特币。 + - 对于大额资金或共享账户,单一签名的安全性不足以抵御潜在风险。 + +3. **多重签名需求**: + - 多重签名(multisig)技术允许多个签名者共同管理一个比特币地址,提高了安全性。 + - 特别适用于企业账户、家庭共享账户和其他需要联合管理的情境。 + +### 具体的 BIP + +1. **BIP 11**: + - 标题:`M-of-N Standard Transactions` + - 提案作者:Gavin Andresen + - 提出时间:2012 年 3 月 + - 内容:描述了多重签名交易的标准格式,即 M 个签名中的 N 个签名验证通过即可执行交易。 + +2. **BIP 16**: + - 标题:`Pay to Script Hash` + - 提案作者:Gavin Andresen + - 提出时间:2012 年 1 月 + - 内容:定义了支付到脚本哈希(P2SH)的机制,使得复杂的脚本可以更简洁地在链上表示,从而增强了多重签名的可用性。 + +### 时间线 + +1. **2011 年至 2012 年初**: + - 多重签名的需求逐渐显现,社区开始讨论如何实现更安全的交易机制。 + +2. **2012 年 1 月**: + - Gavin Andresen 提出 BIP 16,描述了 P2SH 的机制,为多重签名交易的简化提供了基础。 + +3. **2012 年 3 月**: + - Gavin Andresen 提出 BIP 11,明确了 M-of-N 多重签名交易的标准格式,具体描述了如何在比特币脚本中实现多重签名验证。 + +4. **2012 年 5 月**: + - 比特币客户端(Bitcoin Core)集成了 P2SH 和多重签名功能,`OP_CHECKMULTISIG` 操作码开始被正式使用。 + +### 关键人物 + +1. **Gavin Andresen**: + - 比特币基金会的首席科学家,最早提出并推动了 P2SH 和多重签名交易的标准化。 + - 他是 BIP 11 和 BIP 16 的主要作者,对 `OP_CHECKMULTISIG` 的引入和实施起到了关键作用。 + +2. **比特币开发社区**: + - 包括许多开发者和贡献者,他们共同讨论、审查和改进了多重签名机制。 + - 社区的参与和支持确保了这些改进提案的顺利实施和推广。 + +### 工作原理 + +`OP_CHECKMULTISIG` 的基本原理是检查一组公钥和签名,确认至少有 M 个签名是有效的。具体来说,这个操作码会验证堆栈上的公钥和签名,并确保至少有 M 个签名与对应的公钥匹配。 + +### 操作步骤 + +1. **堆栈输入**: + - 签名数量(N) + - N 个签名 + - 公钥数量(M) + - M 个公钥 + +2. **执行过程**: + - 从堆栈中弹出签名数量(N)。 + - 弹出 N 个签名并存储。 + - 弹出公钥数量(M)。 + - 弹出 M 个公钥并存储。 + - 验证至少有 M 个签名能匹配对应的公钥。 + - 如果验证成功,脚本继续执行;否则,脚本失败。 + +### 示例脚本 + +假设我们有三个公钥,需要至少两个签名来验证交易,脚本如下: + +``` +2 3 OP_CHECKMULTISIG +``` + +在这个脚本中: +- `2` 表示需要两个有效签名。 +- ` ` 是三个公钥。 +- `3` 表示总共有三个公钥。 +- `OP_CHECKMULTISIG` 执行多重签名验证。 + +### 例子详解 + +假设 Alice、Bob 和 Charlie 共同管理一个地址,任何交易需要至少两人签名。我们生成一个多重签名地址,并创建一个 P2SH(Pay to Script Hash)交易。 + +1. **创建多重签名地址**: + + ``` + 2 3 OP_CHECKMULTISIG + ``` + +2. **生成 P2SH 地址**: + 将上述脚本的哈希作为地址,这样可以简化交易脚本并提高安全性。 + +3. **花费多重签名地址中的资金**: + - Alice 和 Bob 签名交易。 + - 将签名和原始脚本放入输入脚本中: + + ``` + 0 2 3 OP_CHECKMULTISIG + ``` + +### 注意事项 + +- **漏洞**:早期版本的 `OP_CHECKMULTISIG` 有一个“off-by-one”漏洞,要求在签名数量前添加一个额外的 `OP_0`。 +- **效率**:验证签名和公钥需要计算资源,在高频交易场景中需要考虑其效率。 + +## 链上交易 +### P2SH-P2MS +链上交易:https://mempool.space/zh/tx/45f862a3c72b659406c38f606e2bcc73145241c3bf5c42ded4d4737af8d401ad +```js +// ScriptSig + +OP_0 +OP_PUSHBYTES_71 3044022018a750079fc75b68f5208f86b146821d5f3acb41f68195f8b4cbf1012cba2cbb02200c3ddd0dc271342f62222603356dd7c713cf387718e57ec2644f7811295d03ea01 +OP_PUSHBYTES_72 30450221009a5914969675238ddfb9c1475d2888b3a5a507c6d13c35ac13a6f97a5c3dec97022055587f56408ba7abdc48d637d3eb03a3c29f05d7c22df9a20fd4bea739a00b2601 +OP_PUSHDATA1 52210231d0ed51696dff4a25015f2a8330d07b81761eda22cae558da1086b5d084e1922102e142c9123d48ce4c0b32403b19403e41129f1d37de852d9bbc95165af6960e3b2102207620c770dc24eb02edc6fba51b70fd0c04a2772842d808188b835a8f73832853ae + +// P2SH redeem script +OP_PUSHNUM_2 +OP_PUSHBYTES_33 0231d0ed51696dff4a25015f2a8330d07b81761eda22cae558da1086b5d084e192 +OP_PUSHBYTES_33 02e142c9123d48ce4c0b32403b19403e41129f1d37de852d9bbc95165af6960e3b +OP_PUSHBYTES_33 02207620c770dc24eb02edc6fba51b70fd0c04a2772842d808188b835a8f738328 +OP_PUSHNUM_3 +OP_CHECKMULTISIG +``` + +### P2WSH-P2MS +链上交易:https://mempool.space/zh/tx/a085e629f5932e729f54f174c936f015a10bb7935db66d5803e81dbff0ee15cc +```js +// Witness + +304402204f63983ae7cdb18fc3b75d4acc8e74d35816d4a8660d4296da4c0f6a0206fdd90220347e8317c686aa3785905eb1ff1cf32c4ce7ef019678fdd9ad00cfceedd74c2e01 +30440220055d4a41afa73ef449b18add7748b054eec768a8350c5095b4f622546eb55a3f022073cea484b99331cc8ae1ebf6b1698d07b560ad01ee63e3a4a739684a0408aec601 +52210279d1f38c1c80d47cb00ddbbe2915a60d5706e1ef66056a169150f083b288eb952102cb7d02b654f8616bfc5ab017b7a3ec9092e466381af0f552b7efcd8d920453672103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae + +// P2WSH witness script +OP_PUSHNUM_2 +OP_PUSHBYTES_33 0279d1f38c1c80d47cb00ddbbe2915a60d5706e1ef66056a169150f083b288eb95 +OP_PUSHBYTES_33 02cb7d02b654f8616bfc5ab017b7a3ec9092e466381af0f552b7efcd8d92045367 +OP_PUSHBYTES_33 03c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f880 +OP_PUSHNUM_3 +OP_CHECKMULTISIG +``` + +### P2TR-P2MS + +## 代码示例 + +完整的示例可以在[bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib/)里找到,这里逐步拆解分析。 + +### P2SH-P2MS + +示例代码参考[bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts#L204-L250)。 + + +1. **构建锁定脚本**: + 以上面的示例脚本为例: + + ```javascript + 2 3 OP_CHECKMULTISIG + + const output = bscript.compile( + ([] as Stack).concat( + OP_INT_BASE + a.m, + a.pubkeys, + OP_INT_BASE + o.n, + OPS.OP_CHECKMULTISIG, + ) + ); + + // 其中 + m = 2; + pubkeys = [, , ]; + n = 3; + ``` + +2. **签名过程**: + + ```javascript + const multisig = createPayment('p2sh-p2ms(2 of 4)'); + const inputData1 = await getInputData(2e4, multisig.payment, false, 'p2sh'); + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData1) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 1e4, + }) + .signInput(0, multisig.keys[0]) + .signInput(0, multisig.keys[2]); + ``` + + 签名之后会得到签名信息,多签对应多个元素的签名数组: + + ```javascript + const partialSig = [ + { + pubkey: AlicePubKey, + signature: bscript.signature.encode(Alice.sign(hash), sighashType), + }, + { + pubkey: BobPubKey, + signature: bscript.signature.encode(Bob.sign(hash), sighashType), + }, + ]; + ``` + +3. **排序签名**: + + ```javascript + function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] { + const p2ms = payments.p2ms({ output: script }); + // for each pubkey in order of p2ms script + return p2ms + .pubkeys!.map(pk => { + // filter partialSig array by pubkey being equal + return ( + partialSig.filter(ps => { + return ps.pubkey.equals(pk); + })[0] || {} + ).signature; + // Any pubkey without a match will return undefined + // this last filter removes all the undefined items in the array. + }) + .filter(v => !!v); + } + ``` + +4. **构建解锁脚本**: + + ```javascript + bscript.compile(([OPS.OP_0] as Stack).concat(a.signatures)) + ``` + + \ No newline at end of file diff --git a/BTC/Advanced/Multisign/OP_CHECKMULTISIGVERIFY/README.md b/BTC/Advanced/Multisign/OP_CHECKMULTISIGVERIFY/README.md new file mode 100644 index 000000000..c3a6ac404 --- /dev/null +++ b/BTC/Advanced/Multisign/OP_CHECKMULTISIGVERIFY/README.md @@ -0,0 +1,48 @@ +## OP_CHECKMULTISIGVERIFY + +`OP_CHECKMULTISIGVERIFY` 并没有通过单独的 BIP 提出,但其背景和实现与 BIP 16 的提出密切相关。BIP 16 提出了支付到脚本哈希(P2SH)的机制,使得复杂的脚本可以更简洁地在链上表示,并且增强了多重签名的可用性。 + +### 详细背景 + +#### BIP 16 提出 + +1. **BIP 16**: + - 标题:`Pay to Script Hash` + - 提案作者:Gavin Andresen + - 提出时间:2012 年 1 月 + - 内容:定义了支付到脚本哈希(P2SH)的机制。P2SH 允许复杂的脚本在链上以更简洁的形式表示,并在解锁时提供完整的脚本内容。这极大地简化了多重签名等复杂交易的实现。 + +#### OP_CHECKMULTISIGVERIFY 的引入 + +在 BIP 16 提出并实现后,比特币开发社区继续寻找优化和简化脚本的方法。`OP_CHECKMULTISIGVERIFY` 作为 `OP_CHECKMULTISIG` 和 `OP_VERIFY` 的组合,自然而然地被引入,以简化多重签名验证的脚本编写和执行过程。 + +### OP_CHECKMULTISIGVERIFY 的工作原理 + +`OP_CHECKMULTISIGVERIFY` 结合了 `OP_CHECKMULTISIG` 和 `OP_VERIFY` 的功能。其主要目的是在验证多重签名后,立即判断验证结果并返回执行状态。 + +脚本构建和签名过程可以参考[OP_CHECKMULTISIG](../OP_CHECKMULTISIG/README.md),这里不再赘述。 + +### 具体案例 +在babylon业务中,会把用户的资产质押到一个Taproot地址里。根据官方披露的三条解锁路径都需要OP_CHECKSIGVERIFY来验证签名然后执行后续逻辑 +1. 时间锁路径,质押者到期可以赎回 +```js + OP_CHECKSIGVERIFY OP_CHECKSEQUENCEVERIFY +``` + +2. 解绑路径,质押者提前赎回 +``` + OP_CHECKSIGVERIFY + OP_CHECKSIG + OP_CHECKSIGADD ... OP_CHECKSIGADD + OP_GREATERTHANOREQUAL +``` + +3. 质押者作恶,惩罚路径 +```js + OP_CHECKSIGVERIFY + OP_CHECKSIGVERIFY + OP_CHECKSIG OP_CHECKSIGADD ... OP_CHECKSIGADD + OP_GREATERTHANOREQUAL +``` + +相关交易可以查看:https://mempool.space/zh/signet/tx/ceb126550481ecb69b45929b2b5869fd3975a707e6100b368d6cc15e4434ad9d \ No newline at end of file diff --git a/BTC/Advanced/Multisign/OP_CHECKSIGADD/README.md b/BTC/Advanced/Multisign/OP_CHECKSIGADD/README.md new file mode 100644 index 000000000..015ad3420 --- /dev/null +++ b/BTC/Advanced/Multisign/OP_CHECKSIGADD/README.md @@ -0,0 +1,246 @@ +## OP_CHECKSIGADD +`OP_CHECKSIGADD` 是比特币脚本语言中的一个操作码(opcode),其引入主要目的是为了优化和简化多重签名验证过程。它在 Taproot 和 Schnorr 签名方案中扮演了重要角色,允许更高效地实现多重签名和复杂交易。 + +### BIP 提出的背景 + +`OP_CHECKSIGADD` 的引入是为了配合 Taproot 和 Schnorr 签名的实现,这两个方案显著优化了比特币的签名机制,提高了隐私性和效率。 + +#### 具体的 BIP +1. **BIP 340**: + - 标题:`Schnorr Signatures for secp256k1` + - 提案作者:Pieter Wuille, Jonas Nick, Tim Ruffing + - 提出时间:2020 年 1 月 + - 内容:介绍了 Schnorr 签名算法,为比特币带来了更简洁的签名和更高的验证效率。 + +2. **BIP 341**: + - 标题:`Taproot: SegWit version 1 spending rules` + - 提案作者:Pieter Wuille, Jonas Nick, Anthony Towns + - 提出时间:2020 年 1 月 + - 内容:定义了 Taproot 升级,引入了一种新的输出类型,使得复杂的交易更加高效和隐私。 + +3. **BIP 342**: + - 标题:`Validation of Taproot Scripts` + - 提案作者:Pieter Wuille + - 提出时间:2020 年 1 月 + - 内容:引入了 `OP_CHECKSIGADD` 操作码,用于在 Taproot 中实现更高效的多重签名验证。 + +### 关键人物 + +1. **Pieter Wuille**: + - 比特币核心开发者,BIP 340、BIP 341 和 BIP 342 的主要作者,对 Taproot 和 Schnorr 签名的引入和实施起到了关键作用。 + +2. **Jonas Nick**: + - 比特币核心开发者,参与了 Schnorr 签名和 Taproot 的研究和开发。 + +3. **Tim Ruffing**: + - 密码学研究者,参与了 Schnorr 签名和多重签名方案的研究和开发。 + +4. **Anthony Towns**: + - 比特币核心开发者,参与了 Taproot 的设计和实现。 + +### 激活过程 +为了激活 Taproot 升级,比特币网络采用了一种称为“Speedy Trial”的激活机制。这种机制允许矿工通过在区块中设置特定的标志位(version bits)来表示他们对 Taproot 升级的支持。 + +- **信号期**:矿工在每个区块中设置信号位,表示对 Taproot 升级的支持。 +- **激活门槛**:在一个难度调整周期(2016 个区块)内,需要至少 90% 的区块(即 1815 个区块)设置信号位,以表示对升级的支持。 +- **锁定期**:如果在一个信号期内达到了激活门槛,Taproot 升级会进入“锁定期”(lock-in period),再经过一个难度调整周期后,升级正式生效。 + +#### 5. 正式激活 + +Taproot 升级在 2021 年 6 月达到了激活门槛,随后在 2021 年 11 月正式激活。从这个时间点开始,`OP_CHECKSIGADD` 和其他相关的新功能在比特币网络上正式生效。 + +### 关键时间点 + +- **2020 年 1 月**:BIP 340、BIP 341 和 BIP 342 提出。 +- **2021 年 6 月**:Taproot 升级达到了激活门槛。 +- **2021 年 11 月 14 日**:Taproot 升级正式激活。 + +### 工作原理 + +`OP_CHECKSIGADD` 操作码的主要功能是验证单个签名并将结果(0 或 1)与现有的累计值相加。这在构建多重签名验证时尤其有用,因为它允许逐步累积签名验证的结果,然后在最后进行总和检查。 + +#### 操作步骤 + +1. 从堆栈中弹出一个公钥。 +2. 从堆栈中弹出一个签名。 +3. 从堆栈中弹出一个累计值。 +4. 验证公钥和签名: + - 如果签名有效,返回 1;否则返回 0。 +5. 将验证结果与累计值相加。 +6. 将新的累计值压回堆栈。 + +### 示例脚本 + +假设我们有三个公钥和三个对应的签名,我们希望验证至少两个签名,脚本如下: + +``` + <0> OP_CHECKSIGADD OP_CHECKSIGADD OP_CHECKSIGADD <2> OP_EQUAL +``` + +在这个脚本中: +- ` ` 是签名。 +- `<0>` 是初始累计值。 +- ` ` 是公钥。 +- `OP_CHECKSIGADD` 执行单个签名验证并累加结果。 +- `<2> OP_EQUAL` 检查累加值是否等于 2。 + + +`OP_CHECKSIGADD` 是比特币脚本语言中用于优化多重签名验证的新操作码。它简化了多重签名的实现,使得复杂交易更加高效。它的引入与 BIP 340、BIP 341 和 BIP 342 紧密相关,标志着比特币在隐私性和效率方面的显著提升。通过 `OP_CHECKSIGADD`,比特币能够更灵活地实现多重签名验证,同时保持较高的安全性和性能。 + +### 案例 +这里以上面示例中taproot script-path脚本中的2-3多签为例 + +1. 随机生成internal public key和三份多签用户的私钥 +```js +const internalKey = bip32.fromSeed(rng(64), regtest); + +const leafKeys = []; +const leafPubkeys = []; +for (let i = 0; i < 3; i++) { + const leafKey = bip32.fromSeed(rng(64), regtest); + leafKeys.push(leafKey); + leafPubkeys.push(toXOnly(leafKey.publicKey).toString('hex')); +} +``` + +2. 构造锁定脚本 +```js + const leafScriptAsm = `${leafPubkeys[2]} OP_CHECKSIG ${leafPubkeys[1]} OP_CHECKSIGADD ${leafPubkeys[0]} OP_CHECKSIGADD OP_3 OP_NUMEQUAL`; +``` + +3. 构造MAST +```js +const leafScript = bitcoin.script.fromASM(leafScriptAsm); + +const scriptTree: Taptree = [ + { + output: bitcoin.script.fromASM( + '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG', + ), + }, + [ + { + output: bitcoin.script.fromASM( + '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0 OP_CHECKSIG', + ), + }, + { + output: leafScript, + }, + ], +]; +const redeem = { + output: leafScript, + redeemVersion: LEAF_VERSION_TAPSCRIPT, +}; + +const { output, address, witness } = bitcoin.payments.p2tr({ + internalPubkey: toXOnly(internalKey.publicKey), + scriptTree, + redeem, + network: regtest, +}); +``` + +4. 构造PSBT交易 +```js +// amount from faucet +const amount = 42e4; +// amount to send +const sendAmount = amount - 1e4; +// get faucet +const unspent = await regtestUtils.faucetComplex(wallet.output, amount); + +const psbt = new bitcoin.Psbt({ network: regtest }); + +// Adding an input is a bit special in this case, +// So we contain it in the wallet class +// Any wallet can do this, wallet2 or wallet3 could be used. +wallet.addInput(psbt, unspent.txId, unspent.vout, unspent.value); + +psbt.addOutput({ value: sendAmount, address: wallet.address }); + +``` + +5. 签名交易 +```js +// Sign with at least 2 of the 3 wallets. +// Verify that there is a matching leaf script +// (which includes the unspendable internalPubkey, +// so we verify that no one can key-spend it) +wallet3.verifyInputScript(psbt, 0); +wallet2.verifyInputScript(psbt, 0); +psbt.signInput(0, wallet3); +psbt.signInput(0, wallet2); + +// Before finalizing, we need to add dummy signatures for all that did not sign. +// Any wallet can do this, wallet2 or wallet3 could be used. +wallet.addDummySigs(psbt); + +// its just used to add dummy signature that have an empty Buffer +addDummySigs(psbt: bitcoin.Psbt) { + const leafHash = tapleafHash({ + output: this.leafScript, + version: this.leafVersion, + }); + for (const input of psbt.data.inputs) { + if (!input.tapScriptSig) continue; + const signedPubkeys = input.tapScriptSig + .filter(ts => ts.leafHash.equals(leafHash)) + .map(ts => ts.pubkey); + for (const pubkey of this.pubkeys) { + if (signedPubkeys.some(sPub => sPub.equals(pubkey))) continue; + // Before finalizing, every key that did not sign must have an empty signature + // in place where their signature would be. + // In order to do this currently we need to construct a dummy signature manually. + input.tapScriptSig.push({ + // This can be reused for each dummy signature + leafHash, + // This is the pubkey that didn't sign + pubkey, + // This must be an empty Buffer. + signature: Buffer.from([]), + }); + } + } +} +``` +这里需要注意 +OP_CHECKSIGADD需要**完整的三个签名**,我们需要对未签名的公钥补一个无效签名,即一个字节的0x00 + +6. 提取广播 +```js +psbt.finalizeAllInputs(); +const tx = psbt.extractTransaction(); +const rawTx = tx.toBuffer(); +const hex = rawTx.toString('hex'); + +await regtestUtils.broadcast(hex); +``` + +OP_CHECKSIGADD交易示例,这笔交易里还同时包含了OP_CHECKSIG和OP_CHECKSIGVERIFY:https://mempool.space/zh/signet/tx/ceb126550481ecb69b45929b2b5869fd3975a707e6100b368d6cc15e4434ad9d + +```js +OP_PUSHBYTES_32 9d65bad2d86e26bf1a907077312b849f89e5109a8574087a34953252af63940c +OP_CHECKSIGVERIFY +OP_PUSHBYTES_32 113c3a32a9d320b72190a04a020a0db3976ef36972673258e9a38a364f3dc3b0 +OP_CHECKSIG +OP_PUSHBYTES_32 17921cf156ccb4e73d428f996ed11b245313e37e27c978ac4d2cc21eca4672e4 +OP_CHECKSIGADD +OP_PUSHBYTES_32 3bb93dfc8b61887d771f3630e9a63e97cbafcfcc78556a474df83a31a0ef899c +OP_CHECKSIGADD +OP_PUSHBYTES_32 40afaf47c4ffa56de86410d8e47baa2bb6f04b604f4ea24323737ddc3fe092df +OP_CHECKSIGADD +OP_PUSHBYTES_32 49766ccd9e3cd94343e2040474a77fb37cdfd30530d05f9f1e96ae1e2102c86e +OP_CHECKSIGADD +OP_PUSHBYTES_32 76d1ae01f8fb6bf30108731c884cddcf57ef6eef2d9d9559e130894e0e40c62c +OP_CHECKSIGADD +OP_PUSHBYTES_32 79a71ffd71c503ef2e2f91bccfc8fcda7946f4653cef0d9f3dde20795ef3b9f0 +OP_CHECKSIGADD +OP_PUSHBYTES_32 d21faf78c6751a0d38e6bd8028b907ff07e9a869a43fc837d6b3f8dff6119a36 +OP_CHECKSIGADD +OP_PUSHBYTES_32 f5199efae3f28bb82476163a7e458c7ad445d9bffb0682d10d3bdb2cb41f8e8e +OP_CHECKSIGADD +OP_PUSHNUM_6 +OP_NUMEQUAL +``` \ No newline at end of file diff --git a/BTC/Advanced/Multisign/README.md b/BTC/Advanced/Multisign/README.md new file mode 100644 index 000000000..e346c0d80 --- /dev/null +++ b/BTC/Advanced/Multisign/README.md @@ -0,0 +1,117 @@ +## 多签介绍 +在比特币 (BTC) 网络中,多签名 (Multi-Signature 或 Multi-Sig) 是一种安全性增强技术,它要求多个密钥签名才能执行一笔交易。具体来说,多签机制允许用户创建一个钱包地址,只有在满足一定数量的签名条件下,才能使用钱包中的资金。多签名技术可以用于提高安全性、实现分布式控制和管理资金等用途。 + +### 多签名的工作原理 +1. **创建多签地址**: + - 多签地址通常是由多个参与者的公钥共同生成的。 + - 比如,一个 2-of-3 的多签地址意味着需要三个公钥,并且至少需要其中两个公钥对应的私钥来签名交易。 + +2. **构造交易**: + - 当需要使用多签地址中的比特币时,必须构造一个包含所有必要签名的交易。 + - 比如,对于 2-of-3 的多签地址,至少需要两个参与者的签名才能执行交易。 + +3. **广播交易**: + - 完整签名的交易可以广播到比特币网络,并由矿工进行验证和打包。 + +### 多签名的应用场景 +1. **提高安全性**: + - 单一私钥被盗将无法转移资金,必须多个私钥同时被盗才能实现恶意交易。 + +2. **分布式控制**: + - 可以用于多方共同控制资金,比如公司资金的多方管理,防止单人滥用资金。 + +3. **智能合约和去中心化应用**: + - 多签名可以作为简单的智能合约机制,支持更加复杂的交易条件和逻辑。 + +### 示例 +一个简单的 2-of-3 多签交易流程可能如下: +1. 生成三个公私钥对(用户A、用户B、用户C)。 +2. 使用三个公钥生成一个 2-of-3 的多签地址。 +3. 将比特币转入这个多签地址。 +4. 当需要转出比特币时,用户A和用户B(或者用户A和用户C,或者用户B和用户C)共同签名交易。 +5. 交易广播到网络并等待确认。 + + +## 多签验证的OPCode +在比特币网络中,多签名(Multi-Signature,简称多签)是一种提高安全性和灵活性的技术,允许在执行交易时需要多个签名。为了实现多签名,比特币脚本(Bitcoin Script)提供了几个关键的操作码(OPCode)。以下是与多签名相关的主要 OPCode 及其功能: + +### 主要的多签名 OPCode + +1. **OP_CHECKMULTISIG** + - **功能**:验证多签名交易。 + - **描述**:从堆栈中弹出 n 个公钥和 m 个签名,检查是否有至少 m 个签名有效。如果有效,则返回 true,否则返回 false。 + - **示例**: + ``` + OP_CHECKMULTISIG + ``` + 其中,`` 是需要的最小签名数,`` 是提供的公钥数量。 + +2. **OP_CHECKMULTISIGVERIFY** + - **功能**:与 OP_CHECKMULTISIG 类似,但在验证失败时终止执行并返回错误。 + - **描述**:与 OP_CHECKMULTISIG 的工作原理相同,只是如果检查失败,脚本将立即失败。 + - **示例**: + ``` + OP_CHECKMULTISIGVERIFY + ``` + +3. **OP_CHECKSIGADD** + - **功能**:用于在 Taproot 和 Schnorr 签名方案中实现高效的多签名验证。 + - **描述**:从堆栈中取出一个公钥和一个签名,验证该签名是否有效,并将结果添加到一个计数器中。计数器表示有效签名的总数。 + - **示例**: + ``` + <签名A> OP_CHECKSIGADD + <签名B> OP_CHECKSIGADD + <签名C> OP_CHECKSIGADD + <2> OP_EQUAL + ``` + - ` <签名A> OP_CHECKSIGADD`:验证签名A是否匹配A公钥并将结果加到计数器。 + - ` <签名B> OP_CHECKSIGADD`:验证签名B是否匹配B公钥并将结果加到计数器。 + - ` <签名C> OP_CHECKSIGADD`:验证签名C是否匹配C公钥并将结果加到计数器。 + - `<2> OP_EQUAL`:检查计数器是否等于2,即验证是否有两个有效签名。 + +### 示例脚本 + +#### P2SH(Pay-to-Script-Hash)多签名交易 +1. **创建赎回脚本(Redeem Script)**: + ``` + <2> <3> OP_CHECKMULTISIG + ``` + - `<2>`:至少需要两个签名。 + - ` `:三个公钥。 + - `<3>`:表示共有三个公钥。 + +2. **生成 P2SH 地址**: + - 将上述赎回脚本的哈希值作为 P2SH 地址,将比特币发送到该地址。 + +3. **花费 P2SH 多签名交易**: + - 解锁脚本(Unlocking Script): + ``` + <0> <签名1> <签名2> <赎回脚本> + ``` + - `<0>`:因为 OP_CHECKMULTISIG 有一个历史遗留的 bug,需要额外的空值。 + - `<签名1> <签名2>`:两个有效签名。 + - `<赎回脚本>`:实际的赎回脚本,证明这是正确的解锁条件。 + +#### Taproot 多签名脚本 +1. **赎回脚本(Taproot Script)**: + ``` + <签名A> OP_CHECKSIGADD + <签名B> OP_CHECKSIGADD + <签名C> OP_CHECKSIGADD + <2> OP_EQUAL + ``` + +### 操作码对比 +对比比特币多签名相关的主要 OPCode,包括功能、描述、优点、缺点、使用的脚本类型、推出的 BIP 编号及示例: + +| OPCode | 功能 | 描述 | 优点 | 缺点 | 使用的脚本类型 | 推出的 BIP 编号 | 示例 | +|--------------------------|---------------------------|---------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|-------------------------------------------------------------------------|--------------------------------|------------------|---------------------------------------------------------------------------------------------------------------| +| **[OP_CHECKMULTISIG](./OP_CHECKMULTISIG/README.md)** | 验证多签名交易 | 从堆栈中弹出 n 个公钥和 m 个签名,检查是否有至少 m 个签名有效。如果有效,则返回 true,否则返回 false。 | 适用于传统多签名交易,广泛支持 | 脚本较大,存在历史遗留的 bug 需要额外的空值 | P2SH, P2WSH | BIP 11, BIP 16 | `<2> <3> OP_CHECKMULTISIG` | +| **[OP_CHECKMULTISIGVERIFY](./OP_CHECKMULTISIGVERIFY/README.md)** | 验证多签名并返回错误 | 与 OP_CHECKMULTISIG 类似,但在验证失败时终止执行并返回错误。 | 简化了错误处理逻辑 | 同样存在历史遗留的 bug 需要额外的空值 | P2SH, P2WSH | BIP 11, BIP 16 | `<2> <3> OP_CHECKMULTISIGVERIFY` | +| **[OP_CHECKSIGADD](./OP_CHECKSIGADD/README.md)** | 高效验证多签名 | 从堆栈中取出一个公钥和一个签名,验证该签名是否有效,并将结果添加到一个计数器中。计数器表示有效签名的总数。 | 更高效,简化了多签名脚本,减少交易大小和费用 | 仅在 Taproot 和 Schnorr 签名方案中可用 | Taproot | BIP 340, BIP 341 | ` <签名A> OP_CHECKSIGADD
<签名B> OP_CHECKSIGADD
<签名C> OP_CHECKSIGADD
<2> OP_EQUAL` | + + + +### 总结 +通过这些操作码,比特币网络提供了多种实现多签名的方式。`OP_CHECKMULTISIG` 和 `OP_CHECKMULTISIGVERIFY` 是传统的多签名实现方式,而 `OP_CHECKSIGADD` 则是引入 Taproot 和 Schnorr 签名后,更高效和灵活的实现方式。这些技术共同提升了比特币交易的安全性和灵活性,满足了不同应用场景的需求。 + diff --git a/BTC/Advanced/OP_CAT/README.md b/BTC/Advanced/OP_CAT/README.md new file mode 100644 index 000000000..18f6288e0 --- /dev/null +++ b/BTC/Advanced/OP_CAT/README.md @@ -0,0 +1,46 @@ +`OP_CAT` 是比特币脚本中的一个操作码(opcode),它用于将两个或多个元素连接在一起。这一功能在比特币的原生脚本语言中并不存在,因此 `OP_CAT` 的引入是为了增强比特币脚本的灵活性和功能,特别是在智能合约和复杂交易的应用场景中。 + +### `OP_CAT` 的背景和用途 + +1. **操作码(Opcode)概述**: + - 比特币的脚本语言是栈式的,操作码是脚本的基本组成部分。每个操作码执行特定的功能,如加法、签名验证和数据操作等。 + - `OP_CAT` 用于将两个栈顶元素连接(concatenate)成一个更大的元素。这对于构建复杂数据结构和处理更复杂的脚本逻辑非常有用。 + +2. **为何需要 `OP_CAT`**: + - 在比特币早期的脚本语言中,没有直接的连接字符串或数据的功能。这限制了某些智能合约和复杂交易的实现。 + - 引入 `OP_CAT` 使得开发者能够更灵活地操作和组合数据,增强了比特币脚本的表达能力。 + +### `OP_CAT` 的功能 + +- **连接功能**:`OP_CAT` 将两个元素从栈中取出,并将它们连接成一个新的元素。举例来说,如果栈顶的元素是字符串 "Hello" 和 "World",执行 `OP_CAT` 后,栈顶元素将变成 "HelloWorld"。 +- **应用场景**: + - **智能合约**:在构建智能合约时,可以利用 `OP_CAT` 组合数据,形成更复杂的数据结构。 + - **多签名交易**:在多签名场景中,可以将多个公钥或签名连接在一起,以便在验证时进行处理。 + - **数据打包**:当需要将多个输入数据合并成一个输出时,`OP_CAT` 是一个重要工具。 + +### 示例 + +假设我们有两个元素在栈中,分别为 "Data1" 和 "Data2"。使用 `OP_CAT` 的过程如下: + +1. **栈状态**: + ``` + Top -> Data2 + Data1 + ``` + +2. **执行 `OP_CAT`**: + - 栈顶的两个元素 "Data1" 和 "Data2" 被取出,并连接在一起。 + +3. **结果栈状态**: + ``` + Top -> Data1Data2 + ``` + +### 安全性和实现 + +- **安全性**:虽然 `OP_CAT` 增强了脚本的灵活性,但也可能引入一些复杂性。合约的开发者需要确保合约逻辑的安全性和正确性,避免潜在的漏洞。 +- **实现**:`OP_CAT` 的实现需要对比特币的脚本引擎进行修改,因此需要在比特币核心代码中进行添加和测试,确保它与现有的操作码兼容。 + +### 结论 + +`OP_CAT` 的引入为比特币的脚本语言增加了重要的功能,使得在智能合约和复杂交易的开发中可以更灵活地处理数据。尽管比特币的脚本设计注重安全性和简单性,但通过引入 `OP_CAT`,开发者能够实现更丰富的逻辑和应用场景。随着比特币生态系统的发展,像 `OP_CAT` 这样的操作码可能会变得越来越重要。 \ No newline at end of file diff --git a/BTC/Advanced/OP_CAT/example/README.md b/BTC/Advanced/OP_CAT/example/README.md new file mode 100644 index 000000000..2cc2548f7 --- /dev/null +++ b/BTC/Advanced/OP_CAT/example/README.md @@ -0,0 +1,60 @@ +你是对的,我在之前的示例中没有明确给出与 `OP_EQUAL` 验证的内容。在比特币脚本中,`OP_EQUAL` 用于验证栈顶两个元素是否相等。因此,我们需要在脚本中明确指定连接后的结果,并与这个结果进行比较。 + +### 完整示例 + +下面是一个更新后的示例,展示如何使用 `OP_CAT` 实现智能合约,并在脚本中验证连接后的结果是否等于 "HelloWorld": + +```javascript +const bitcoin = require('bitcoinjs-lib'); + +// 创建一个包含 OP_CAT 的脚本 +function createScript() { + // 连接的目标结果 + const target = Buffer.from('HelloWorld'); + + const script = bitcoin.script.compile([ + bitcoin.opcodes.OP_DUP, + bitcoin.opcodes.OP_HASH160, + Buffer.from('...'), // 使用适当的公钥哈希 + bitcoin.opcodes.OP_EQUALVERIFY, + bitcoin.opcodes.OP_CHECKSIG, + // 连接两个字符串 + Buffer.from('Hello'), // 第一个元素 + Buffer.from('World'), // 第二个元素 + bitcoin.opcodes.OP_CAT, // 连接操作 + target, // 连接后的目标结果 + bitcoin.opcodes.OP_EQUAL // 验证连接结果是否等于 'HelloWorld' + ]); + + return script; +} + +// 创建和打印 P2SH 地址 +function createP2SHAddress() { + const script = createScript(); + const { address } = bitcoin.payments.p2sh({ redeem: { output: script, network: bitcoin.networks.bitcoin } }); + + console.log('P2SH Address:', address); +} + +createP2SHAddress(); +``` + +### 代码解析 + +1. **连接后的目标结果**: + - 我们定义了一个 `target` 变量,表示连接后的目标结果 `HelloWorld`。在脚本中,我们将这个目标结果与连接后的结果进行比较。 + +2. **脚本逻辑**: + - `OP_CAT` 将 `Buffer.from('Hello')` 和 `Buffer.from('World')` 连接起来,结果是 `HelloWorld`。 + - 然后,`target`(即 `HelloWorld`)被推入栈中。 + - 最后,`OP_EQUAL` 用于检查连接的结果是否与 `target` 相等。 + +### 注意事项 + +- **脚本验证**:在比特币的环境中,脚本通常是在执行交易时被验证。因此,这段脚本需要在合适的环境中进行测试。 +- **公钥哈希**:在实际应用中,`Buffer.from('...')` 应替换为实际的公钥哈希。 + +### 结论 + +现在这个脚本清楚地验证了连接后的结果是否与预期值 `HelloWorld` 相等。这种方式展示了如何在比特币脚本中使用 `OP_CAT` 进行数据连接和验证。虽然比特币的脚本语言有一定的限制,但通过合理组合操作码,依然可以实现复杂的逻辑。 \ No newline at end of file diff --git a/BTC/Advanced/OP_CAT/example/index.js b/BTC/Advanced/OP_CAT/example/index.js new file mode 100644 index 000000000..cfcdac613 --- /dev/null +++ b/BTC/Advanced/OP_CAT/example/index.js @@ -0,0 +1,33 @@ +const bitcoin = require('bitcoinjs-lib'); + +// 创建一个包含 OP_CAT 的脚本 +function createScript() { + // 连接的目标结果 + const target = Buffer.from('HelloWorld'); + + const script = bitcoin.script.compile([ + bitcoin.opcodes.OP_DUP, + bitcoin.opcodes.OP_HASH160, + Buffer.from('...'), // 使用适当的公钥哈希 + bitcoin.opcodes.OP_EQUALVERIFY, + bitcoin.opcodes.OP_CHECKSIG, + // 连接两个字符串 + Buffer.from('Hello'), // 第一个元素 + Buffer.from('World'), // 第二个元素 + bitcoin.opcodes.OP_CAT, // 连接操作,必须在两个字符串之后 + target, // 连接后的目标结果 + bitcoin.opcodes.OP_EQUAL, // 验证连接结果是否等于 'HelloWorld' + ]); + + return script; +} + +// 创建和打印 P2SH 地址 +function createP2SHAddress() { + const script = createScript(); + const { address } = bitcoin.payments.p2sh({ redeem: { output: script, network: bitcoin.networks.bitcoin } }); + + console.log('P2SH Address:', address); +} + +createP2SHAddress(); diff --git a/BTC/Advanced/Psbt/README.md b/BTC/Advanced/Psbt/README.md new file mode 100644 index 000000000..c23c20677 --- /dev/null +++ b/BTC/Advanced/Psbt/README.md @@ -0,0 +1,59 @@ +PSBT(Partially Signed Bitcoin Transaction,部分签名比特币交易)是一种交易格式,用于在不同参与者之间安全地传输未完成的交易信息,以便完成签名过程。这种格式主要用于提高比特币交易的灵活性和多方合作的安全性,尤其是在涉及多重签名和复杂的交易结构时。 + +### PSBT的目的和应用场景 + +PSBT(Partially Signed Bitcoin Transactions)是一种格式,用于Bitcoin交易的离线或多方签名。这种格式在[BIP 174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki)中被详细定义,该BIP是由Andrew Chow于2017年提出的,PSBT编码和解析参考[PSBT encode](./encode.md)。 + +在PSBT出现之前,Bitcoin的交易签名过程通常需要交易的所有参与者在线,并能够直接交互以产生完全签名的交易。这在多方需要合作创建一个交易,尤其是在使用硬件钱包或冷存储方案时,会变得复杂和不安全。此外,对于复杂的交易类型(如多重签名或条件交易),简单的交易签名数据结构并不足以容纳所需的全部信息,如前一交易的脚本或其他元数据。 + +PSBT的设计初衷是解决比特币生态中的一个关键问题:在不共享私钥的情况下,如何在多个参与者之间安全地协作构建和签名交易。它允许每个参与者独立地添加到交易的签名或其他必需信息,而不必暴露自己的私钥给其他人。 + +主要应用场景包括: +- **多重签名交易**:多个参与者需要对同一笔交易进行签名。 +- **冷存储和硬件钱包**:安全设备可以在不直接连接到互联网的情况下签名交易。 +- **复杂的交易类型**:涉及时间锁、多步骤执行等特殊条件的交易。 + +## PSBT的工作原理 +在PSBT(Partially Signed Bitcoin Transaction)的工作流程中,涉及到几个关键的参与者或身份,他们各自负责交易的不同阶段。这些身份通常根据他们在交易签名和构建过程中的角色来定义。以下是PSBT流程中几个主要身份的概述: + +### 1. 交易发起者(Creator) +- **角色**:开始PSBT流程的是交易发起者。他们负责构建交易的初始结构,这包括定义交易的输入和输出。 +- **责任**:交易发起者需要收集并添加所有必要的输入数据(如UTXOs的详细信息)以及输出信息。他们会创建一个PSBT文件,此文件还未包含任何签名。 + +### 2. 更新者(Updater) +- **角色**:更新者的任务是添加或更新交易中缺失的信息,这可能包括输入和输出的附加细节。 +- **责任**:更新者确保PSBT包含足够的信息以便所有参与者可以验证交易。例如,他们可能需要添加redeemScript或witnessScript等信息。 + +### 3. 签名者(Signer) +- **角色**:签名者是那些用自己的私钥对交易的一个或多个输入进行签名的参与者。 +- **责任**:每个签名者都必须独立验证他们即将签名的交易的有效性和完整性。在确认无误后,签名者将他们的签名添加到PSBT中。 + +### 4. 合并者(Combiner) +- **角色**:在多签名或多步骤签名的情况下,合并者负责将来自不同签名者的多个PSBT副本合并成一个包含所有必需签名的单一PSBT。 +- **责任**:合并者需确保所有的签名都正确并被妥善地合并到PSBT中,没有遗漏任何有效签名。 + +### 5. 最终化者(Finalizer) +- **角色**:最终化者负责处理交易的最终细节,确保所有的输入都已正确地签名,并将PSBT转换成可以广播的标准比特币交易格式。 +- **责任**:最终化者将完成的签名脚本插入到交易中的相应输入,并移除所有不再需要的临时数据。 + +### 6. 广播者(Broadcaster) +- **角色**:一旦PSBT被最终化并转换成一个完整的交易后,广播者负责将其发送到比特币网络。 +- **责任**:广播者需要确保交易被网络接受,并进行监控,直到交易被确认。 + +## PSBT与普通交易的联系与区别 + +| 特性 | 普通交易 (Raw Transaction) | PSBT (Partially Signed Bitcoin Transaction) | +|---------------|-----------------------------------|------------------------------------------------| +| **格式** | 简单的二进制格式 | 扩展的二进制格式,包含未签名交易和元数据 | +| **用途** | 直接在网络上广播和确认 | 用于离线或多方签名,最终转换为普通交易广播 | +| **签名** | 交易中直接包含签名 | 签名被包含在元数据中,允许多步骤和多方签名 | +| **数据内容** | 仅包含交易必需的数据 | 包含交易数据外的额外信息,如脚本、公钥等 | +| **交互性** | 通常由单一实体完成 | 支持多个实体分别签名后合并 | +| **安全性** | 需要网络连接进行签名 | 可以完全离线签名,提高安全性 | +| **兼容性** | 所有比特币网络节点直接支持 | 需要特定的钱包或工具来创建和解析PSBT | +| **处理流程** | 创建、签名、广播为一体的流程 | 创建、签名可以分开,由不同的参与者在不同时间完成 | +| **适用场景** | 简单的个人交易 | 复杂的交易,如多重签名、企业级应用等 | + + + +PSBT是一个强大的工具,能够处理复杂的比特币交易,特别是在需要多方合作的情况下。它通过提供一个标准化且安全的方式来协调和合并不同参与者的输入,极大地增强了比特币交易的多样性和安全性。每个角色在PSBT的创建和完成过程中发挥着关键作用,确保交易不仅符合技术规范,同时也达到了预期的安全性标准。 \ No newline at end of file diff --git a/BTC/Advanced/Psbt/encode.md b/BTC/Advanced/Psbt/encode.md new file mode 100644 index 000000000..8727e992c --- /dev/null +++ b/BTC/Advanced/Psbt/encode.md @@ -0,0 +1,195 @@ +PSBT(Partially Signed Bitcoin Transaction)的编码和解码是一个关键环节,它允许各种工具和应用以标准格式交换交易信息。PSBT格式设计为可扩展和兼容未来功能,同时确保可以在不同设备和平台间安全地传输和处理交易数据。 + +### PSBT格式 + +PSBT的基本格式是一个序列化的数据结构,包含所有必要的信息,使多个参与者能够独立进行交易签名和验证。这个格式遵循一定的编码规则,详细见[BIP174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki),通常以Base64或十六进制形式进行传输。 + +**The Partially Signed Bitcoin Transaction (PSBT) format** + +```bash + # whole structure + := * * + + # start with magic number + # 0x70 0x73 0x62 0x74 encode of PSBT + := 0x70 0x73 0x62 0x74 0xFF + := * 0x00 + := * 0x00 + := * 0x00 + + # keyPair structure + := + + # key structure + := + + # value structure + := +``` + +PSBT数据主要分为以下几部分: + +1. **全局数据**:包括事务的未签名版本,以及可能的全局元数据,例如原始交易的版本和锁定时间。 +2. **输入数据**:每个输入独立的数据,包括UTXO信息、签名脚本、见证脚本、签名和其他相关信息。 +3. **输出数据**:每个输出独立的数据,如赎回脚本和其他可能的输出信息。 + +### 编码过程 + +PSBT的编码过程包括将交易的各个部分转换成可序列化的格式,然后通常使用Base64进行编码以便于传输。具体步骤如下: + +1. **创建基础交易**:首先,创建一个标准的Bitcoin交易,指定所需的输入和输出。 +2. **填充PSBT结构**:为每个输入和输出填充必要的信息,如UTXO详情、赎回路径和必要的签名数据。 +3. **序列化数据**:将所有信息序列化为连续的字节流。这包括使用键值对编码各个部分,其中键和值有特定的格式。 +4. **Base64编码**:最后,将序列化的数据编码为Base64字符串,便于在网络上或通过其他方式安全传输。 + +### 解码过程 + +解码PSBT是编码过程的逆过程,主要步骤包括: + +1. **Base64解码**:首先将接收到的Base64字符串解码回原始的字节流。 +2. **反序列化数据**:解析字节流,根据PSBT的键值对结构恢复出每个部分的数据。 +3. **重建交易信息**:使用解码的数据重建完整的交易视图,包括所有输入和输出的详细信息。 +4. **验证和签名**:在交易数据完全恢复后,参与者可以验证交易的有效性,并在必要时添加自己的签名。 + +## PSBT 创建 + +以下是一个使用JavaScript和`bitcoinjs-lib`库来处理PSBT(Partially Signed Bitcoin Transaction)的基本示例。这个例子展示了如何创建一个PSBT,添加输入和输出,进行签名,并最终导出为可以广播的比特币交易。 + +### 环境设置 + +首先,你需要确保已经安装了`bitcoinjs-lib`。如果尚未安装,可以通过npm进行安装: + +```bash +npm install bitcoinjs-lib +``` + +### 创建和签名PSBT的案例 + +```javascript +const bitcoin = require('bitcoinjs-lib'); + +// 设置网络,这里以比特币测试网为例 +const network = bitcoin.networks.testnet; + +// 创建一个新的比特币密钥对 +const keyPair = bitcoin.ECPair.makeRandom({ network: network }); +const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: network }); + +console.log(`New address: ${address}`); + +// 创建一个新的PSBT +let psbt = new bitcoin.Psbt({ network: network }); + +// 假设这是UTXO的详细信息,你需要从区块链或钱包获取这些信息 +const utxo = { + hash: 'transaction-hash-of-utxo-here', // 事务哈希 + index: 0, // 输出索引 + value: 100000, // Satoshis +}; + +// 添加输入,这里假设UTXO已经知道 +psbt.addInput({ + hash: utxo.hash, + index: utxo.index, + nonWitnessUtxo: Buffer.from('raw-transaction-hex-here', 'hex'), +}); + +// 添加输出,指定接收地址和金额(satoshis) +psbt.addOutput({ + address: 'recipient-address-here', + value: 90000, // 减去手续费 +}); + +// 对PSBT进行签名 +psbt.signInput(0, keyPair); + +// 验证签名是否正确 +psbt.validateSignaturesOfInput(0); + +// 最终确认PSBT +psbt.finalizeAllInputs(); + +// 导出可以广播的交易 +const tx = psbt.extractTransaction().toHex(); + +console.log(`Transaction ready to be broadcasted: ${tx}`); +``` + +### 解释 + +1. **初始化**:首先创建一个比特币网络的环境,并生成一个新的密钥对和地址。 +2. **创建PSBT**:实例化一个PSBT对象,为添加输入和输出做准备。 +3. **添加输入**:添加一个UTXO作为输入。这通常需要事务的原始数据,这里使用`nonWitnessUtxo`字段传入原始交易的hex字符串。 +4. **添加输出**:添加一个输出,指定收款地址和金额。 +5. **签名**:使用私钥对输入进行签名。 +6. **验证签名**:验证输入的签名是否有效。 +7. **完成PSBT**:完成所有输入,锁定PSBT,使之准备好转换为最终的交易格式。 +8. **导出交易**:将PSBT转换为一个标准的比特币交易,并将其转换为hex格式,准备广播到网络。 + +## PSBT 解析 + +要解析PSBT(Partially Signed Bitcoin Transaction)并查看其详细内容,你可以使用`bitcoinjs-lib`库在JavaScript环境中执行这一任务。下面的示例将展示如何读取一个PSBT,解析其内容,以及如何查看关键信息,如输入和输出详情。 + +### 示例:解析PSBT + +```javascript +const bitcoin = require('bitcoinjs-lib'); + +// 假设这是一个已经存在的PSBT的Base64编码字符串 +const psbtBase64 = + 'cHNidP8BAHECAAAAAZtuEiavDmeZR6WUjIhjFbkFh7m+yywCfHlni6uTrgH/////AZD+GwAAAAAAF6kU9BeD5tGIzjL0VzU3+kEUNw+HME3/////8C0wbdQAAAAAAF6kUAAAAAAAAiACATWsYQSE/C85hv5jZVDezl0l4AAAAAAAEBKxAnAAAAAAAAFgAUjVv8Fy0Qdk84W/oBhiMllC+HIHg='; + +// 将Base64编码的PSBT解码 +const psbt = bitcoin.Psbt.fromBase64(psbtBase64); + +// 遍历PSBT的输入和输出 +psbt.data.inputs.forEach((input, index) => { + console.log(`Input ${index}:`); + console.log(input); +}); + +psbt.data.outputs.forEach((output, index) => { + console.log(`Output ${index}:`); + console.log(output); +}); + +// 如果需要更详细地查看某个特定的输入或输出,可以根据实际需要提取更多信息 +``` + +### 解释 + +1. **解码PSBT**:首先使用`bitcoinjs-lib`的`Psbt.fromBase64`方法将Base64编码的PSBT字符串解码成一个PSBT对象。 +2. **遍历输入和输出**:通过访问`psbt.data.inputs`和`psbt.data.outputs`数组,可以查看每个输入和输出的详细信息。这包括UTXO信息、脚本、签名等。 +3. **打印信息**:在控制台打印每个输入和输出的详细信息,这有助于开发者理解和调试PSBT。 + +## 原生解析 + +在比特币的PSBT(Partially Signed Bitcoin Transaction)处理中,原生编码和解码是指不使用任何外部库直接操作PSBT格式数据的方法。这通常涉及到直接处理PSBT的二进制表示,理解其结构,并根据规范来手动解析或构建数据。由于PSBT规范的复杂性,这一过程比较繁琐,但非常重要,因为它能提供对PSBT工作原理的深入理解。 + +### PSBT数据结构 + +PSBT是一种包含多个键值对的数据结构,每个部分都有其特定的分隔符和格式。其结构大致如下: + +1. **全局部分**:包含交易的全局信息,如未签名的交易(除了witness)和其他全局类型的数据。 +2. **输入部分**:每个输入都有自己的键值对集,可能包括非见证UTXO、签名脚本、序列号等。 +3. **输出部分**:每个输出可能包括与之相关的脚本或其他数据。 + +### 原生编码过程 + +编码PSBT时,你需要遵循BIP 174(PSBT规范)中定义的格式,将交易的各个部分转换为键值对,并且每个键值对之后跟一个分隔符。具体步骤如下: + +1. **开始全局部分**:使用特定的魔术字节(`0x70736274FF`,表示"psbt"加上分隔符)开始。 +2. **编码全局键值对**:例如,将交易版本、交易输入和输出数量等编码为键值对。 +3. **输入部分**:对每个输入,编码所有相关信息(如UTXO、见证数据、部分签名等)为键值对。 +4. **输出部分**:对每个输出,编码所有相关信息(如赎回脚本)为键值对。 +5. **结束**:每个部分结束时都需要一个`0x00`作为分隔符。 + +### 原生解码过程 + +解码PSBT时,需要按照编码的相反顺序来操作: + +1. **读取并验证魔术字节**:确认PSBT的开始符合规范。 +2. **解析全局键值对**:读取键值对,直到遇到分隔符。 +3. **逐个解析输入部分**:对于每个输入,读取其键值对,直到遇到分隔符。 +4. **逐个解析输出部分**:对于每个输出,同样读取其键值对。 +5. **验证完整性**:确保所有必要的数据都已正确解析,并且数据结构符合PSBT的规范。 diff --git a/BTC/Advanced/Psbt/rawParsePSBT.js b/BTC/Advanced/Psbt/rawParsePSBT.js new file mode 100644 index 000000000..9a177cd98 --- /dev/null +++ b/BTC/Advanced/Psbt/rawParsePSBT.js @@ -0,0 +1,76 @@ +const parsePSBT = (psbtBase64) => { + const psbtBuffer = Buffer.from(psbtBase64, 'base64'); + let index = 0; + + // 魔术字节和分隔符 + const magicBytes = psbtBuffer.slice(index, index + 5); + index += 5; + if (magicBytes.toString('hex') !== '70736274ff') { // Check for valid PSBT magic bytes + throw new Error('Not a valid PSBT'); + } + + // 解析全局部分 + console.log('Global Data:'); + while (psbtBuffer[index] !== 0x00 && index < psbtBuffer.length) { + // Parse key + if (index + 2 > psbtBuffer.length) break; // Prevent reading beyond buffer + const keyLen = psbtBuffer[index]; + const key = psbtBuffer.slice(index + 1, index + 1 + keyLen); + index += 1 + keyLen; + + // Parse value + if (index + 1 > psbtBuffer.length) break; // Prevent reading beyond buffer + const valueLen = psbtBuffer.readIntLE(index, 1); + const value = psbtBuffer.slice(index + 1, index + 1 + valueLen); + index += 1 + valueLen; + + console.log(`Key: ${key.toString('hex')}, Value: ${value.toString('hex')}`); + } + index++; // Move past separator byte + + // 解析输入部分 + console.log('Inputs:'); + while (index < psbtBuffer.length && psbtBuffer[index] !== 0x00) { + // Process each input's key-value pairs + while (psbtBuffer[index] !== 0x00 && index < psbtBuffer.length) { + if (index + 2 > psbtBuffer.length) break; + const keyLen = psbtBuffer[index]; + const key = psbtBuffer.slice(index + 1, index + 1 + keyLen); + index += 1 + keyLen; + + if (index + 1 > psbtBuffer.length) break; + const valueLen = psbtBuffer.readUIntLE(index, 1); + const value = psbtBuffer.slice(index + 1, index + 1 + valueLen); + index += 1 + valueLen; + console.log(`Key: ${key.toString('hex')}, Value: ${value.toString('hex')}`); + } + index++; // Move to next input or separator + } + index++; // Move past input separator + + // 解析输出部分 + console.log('Outputs:'); + while (index < psbtBuffer.length && psbtBuffer[index] !== 0x00) { + console.log(`Output at index ${index}:`); + // Process each output's key-value pairs + while (psbtBuffer[index] !== 0x00 && index < psbtBuffer.length) { + if (index + 2 > psbtBuffer.length) break; + const keyLen = psbtBuffer[index]; + const key = psbtBuffer.slice(index + 1, index + 1 + keyLen); + index += 1 + keyLen; + + if (index + 1 > psbtBuffer.length) break; + const valueLen = psbtBuffer.readUIntLE(index, 1); + const value = psbtBuffer.slice(index + 1, index + 1 + valueLen); + index += 1 + valueLen; + + console.log(`Key: ${key.toString('hex')}, Value: ${value.toString('hex')}`); + } + index++; // Move to next output or end + } +}; + +// 用你提供的Base64字符串测试 +const psbtBase64 = + 'cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA'; +parsePSBT(psbtBase64); \ No newline at end of file diff --git a/BTC/Advanced/Taproot/MAST.md b/BTC/Advanced/Taproot/MAST.md new file mode 100644 index 000000000..de593c4f4 --- /dev/null +++ b/BTC/Advanced/Taproot/MAST.md @@ -0,0 +1,527 @@ +## MAST简介 + +Merkelized Alternative Script Tree (MAST) 是比特币的一种增强功能,它利用 Merkle 树结构将不同的花费条件(脚本)组织起来,只公开实际使用的脚本分支,提高隐私性和效率。在 MAST 中,有几个关键角色(或身份),它们在构建和验证 Merkle 树时扮演重要作用: + +### 关键角色 + +1. **叶子节点(Leaf Nodes)** + + - **定义**: 叶子节点是 Merkle 树中的基础节点,每个叶子节点代表一个具体的花费条件或脚本。叶子节点通常哈希值进行字典序(lexicographical order)排序。排序的目的是确保 Merkle 树的结构和哈希值是唯一且确定的,这样不同的参与者在构建相同的 Merkle 树时可以得到相同的根哈希值,从而确保交易的一致性和可验证性。 + - **功能**: 包含实际的比特币脚本(例如条件 A、B、C),这些脚本定义了不同的花费条件。 + - **示例**: + - 条件 A: 在特定时间后 Alice 可以花费。 + - 条件 B: 在不同的特定时间后 Bob 可以花费。 + - 条件 C: Alice 和 Bob 联合签名可以立即花费。 + +2. **内部节点(Internal Nodes)** + + - **定义**: 内部节点是 Merkle 树中的中间节点,每个内部节点是其子节点哈希值的组合。 + - **功能**: 通过将其子节点的哈希值组合在一起,内部节点形成了从叶子节点到根节点的路径。 + - **示例**: 如果 `A` 和 `B` 是叶子节点,那么 `H1 = Hash(A, B)` 是内部节点。 + +3. **根节点(Root Node)** + + - **定义**: 根节点是 Merkle 树的顶层节点,代表整个树的哈希值。 + - **功能**: 根节点的哈希值用于验证整个 Merkle 树的完整性和有效性。 + - **示例**: 如果 `H1` 和 `H2` 是内部节点,那么 `Root = Hash(H1, H2)` 是根节点。 + +4. **Merkle 路径(Merkle Path)** + - **定义**: Merkle 路径是从叶子节点到根节点的一系列哈希值,用于验证特定叶子节点是否属于 Merkle 树的一部分。 + - **功能**: 当花费特定条件时,公开的 Merkle 路径允许验证者确认该条件是 Merkle 树的一部分,而不必公开其他条件。 + - **示例**: 如果叶子节点 `C` 被使用,Merkle 路径包括 `H3` 和 `H1`,以验证 `C` 到根节点的路径。 + +### 示例 + +假设有三个条件(脚本),它们组织成如下 Merkle 树结构: + +``` + Root (R) + / \ + H1 H2 + / \ / \ + A B C H3 +``` + +- **A**、**B**、**C** 是叶子节点,代表具体的花费条件。 +- **H1** 是 `Hash(A, B)`,**H2** 是 `Hash(C, H3)`,它们是内部节点。 +- **Root** 是 `Hash(H1, H2)`,它是根节点。 + +当 Alice 和 Bob 联合签名花费条件 C 时,需要公开以下信息来验证条件 C 的合法性: + +1. 条件 C 的脚本(叶子节点)。 +2. 节点 H3(内部节点,用于计算 H2)。 +3. 节点 H1(内部节点,用于计算 Root)。 +4. 根节点 Root。 + +### 验证过程 + +1. 计算 C 的哈希值。 +2. 结合 C 的哈希值和 H3,计算出 H2。 +3. 结合 H1 和 H2,计算出 Root。 +4. 比对计算得到的 Root 和交易中提供的 Root,如果匹配,则验证通过。 + +这种结构使得未使用的条件(例如 A 和 B)保持隐藏,提高了交易的隐私性,同时提供了高效的验证机制。 + +## MAST的创建、更新和移除 + +实现 MAST(Merkelized Alternative Script Tree) 的创建、插入和删除功能,可以使用 JavaScript 结合一些加密库来完成。以下是一个基本的示例,演示如何创建、插入和删除 MAST 中的节点。 + +```javascript +const crypto = require('crypto'); + +class MAST { + constructor() { + this.leaves = []; + this.tree = []; + } + + // 计算哈希值 + hash(data) { + return crypto.createHash('sha256').update(data).digest('hex'); + } + + // 将叶子节点添加到树中 + addLeaf(data) { + const hashedData = this.hash(data); + this.leaves.push(hashedData); + this.buildTree(); + } + + // 删除叶子节点并重建树 + removeLeaf(data) { + const hashedData = this.hash(data); + const index = this.leaves.indexOf(hashedData); + if (index > -1) { + this.leaves.splice(index, 1); + this.buildTree(); + } + } + + // 构建 Merkle 树 + buildTree() { + if (this.leaves.length === 0) { + this.tree = []; + return; + } + + let level = this.leaves.slice().sort(); // 字典序排序 + this.tree = [level]; + + while (level.length > 1) { + level = this.getNextLevel(level); + this.tree.unshift(level); + } + } + + // 计算下一层节点 + getNextLevel(level) { + const nextLevel = []; + for (let i = 0; i < level.length; i += 2) { + if (i + 1 < level.length) { + nextLevel.push(this.hash(level[i] + level[i + 1])); + } else { + nextLevel.push(level[i]); // 如果没有配对节点,直接移动到下一层 + } + } + return nextLevel; + } + + // 获取 Merkle 树的根 + getRoot() { + return this.tree.length ? this.tree[0][0] : null; + } + + // 打印 Merkle 树 + printTree() { + console.log(JSON.stringify(this.tree, null, 2)); + } +} + +// 示例用法 +const mast = new MAST(); +mast.addLeaf('Condition A'); +mast.addLeaf('Condition B'); +mast.addLeaf('Condition C'); +console.log('Initial MAST:'); +mast.printTree(); +console.log('Root:', mast.getRoot()); + +mast.removeLeaf('Condition B'); +console.log('After removing Condition B:'); +mast.printTree(); +console.log('Root:', mast.getRoot()); + +mast.addLeaf('Condition D'); +console.log('After adding Condition D:'); +mast.printTree(); +console.log('Root:', mast.getRoot()); +``` + +### 解释 + +- **hash(data)**: 计算给定数据的 SHA-256 哈希值。 +- **addLeaf(data)**: 添加叶子节点,添加后重建 Merkle 树。 +- **removeLeaf(data)**: 删除叶子节点,删除后重建 Merkle 树。 +- **buildTree()**: 根据当前叶子节点构建 Merkle 树。 +- **getNextLevel(level)**: 计算当前层次节点的哈希值以生成下一层节点。 +- **getRoot()**: 获取 Merkle 树的根节点。 +- **printTree()**: 打印整个 Merkle 树。 + +### 注意事项 + +1. **排序**: 每次构建树时,叶子节点都根据其哈希值进行字典序排序,以确保树的唯一性和确定性。 +2. **树结构**: `tree` 属性是一个数组,存储从叶子节点到根节点的所有层次。 + +这个示例实现了基本的 MAST 功能,包括创建、插入和删除叶子节点。 + +## Taproot中的应用 + +![$$Q = P + t*G$$](https://aandds.com/blog/images/taproot_tweak.gif) +如上图,在Taproot中需要在MAST中针对叶子结点、树结点和根结点分别引入TapLeaf、TapBranch和TapTweak去计算hash。 + +### 代码示例 + +```javascript +const bitcoin = require('bitcoinjs-lib'); +const crypto = require('crypto'); +const ecc = require('tiny-secp256k1'); +const { BIP32Factory } = require('bip32'); +const bip32 = BIP32Factory(ecc); +const { payments } = bitcoin; + +// 创建 Tagged Hash 函数 +function taggedHash(tag, data) { + const tagHash = crypto.createHash('sha256').update(tag).digest(); + return crypto + .createHash('sha256') + .update(Buffer.concat([tagHash, tagHash, data])) + .digest(); +} + +// Taproot Tweak 函数 +function tapTweakPubkey(pubkey, h) { + const tweak = taggedHash('TapTweak', Buffer.concat([pubkey, h])); + const tweakedPubkey = Buffer.from(ecc.pointAddScalar(pubkey, tweak)); + return { tweakedPubkey, tweak }; +} + +// TapLeaf 计算函数 +function tapLeaf(version, script) { + const leafVersion = Buffer.from([version]); + return taggedHash('TapLeaf', Buffer.concat([leafVersion, script])); +} + +// TapBranch 计算函数 +function tapBranch(h1, h2) { + return taggedHash('TapBranch', Buffer.concat([h1, h2].sort(Buffer.compare))); +} + +class MAST { + constructor() { + this.leaves = []; + } + + // 添加叶子节点 + addLeaf(script, version = 0xc0) { + const hashedLeaf = tapLeaf(version, script); + this.leaves.push(hashedLeaf); + this.leaves.sort(Buffer.compare); // 字典序排序 + } + + // 构建 Merkle 树并返回根节点 + getMerkleRoot() { + if (this.leaves.length === 0) { + return Buffer.alloc(32, 0); + } + return this.buildTree(this.leaves); + } + + // 递归构建 Merkle 树 + buildTree(leaves) { + if (leaves.length === 1) { + return leaves[0]; + } + + const nextLevel = []; + for (let i = 0; i < leaves.length; i += 2) { + if (i + 1 < leaves.length) { + nextLevel.push(tapBranch(leaves[i], leaves[i + 1])); + } else { + nextLevel.push(leaves[i]); // 如果没有配对节点,直接移动到下一层 + } + } + + return this.buildTree(nextLevel); + } +} + +// 示例用法 +const mast = new MAST(); +mast.addLeaf(Buffer.from('Condition A')); +mast.addLeaf(Buffer.from('Condition B')); +mast.addLeaf(Buffer.from('Condition C')); +console.log('Initial MAST:'); +console.log(mast.leaves.map((leaf) => leaf.toString('hex'))); +console.log('MAST Root:', mast.getMerkleRoot().toString('hex')); +``` + +### 代码说明 + +1. **taggedHash(tag, data)**: 创建 Tagged Hash 函数,用于 Taproot Tweak 和 TapLeaf/TapBranch。 +2. **tapTweakPubkey(pubkey, h)**: 使用 Tagged Hash 和内部公钥计算 Tweaked 公钥。 +3. **tapLeaf(version, script)**: 计算 TapLeaf 的哈希值。 +4. **tapBranch(h1, h2)**: 计算两个子树的 TapBranch 哈希值。 +5. **MAST 类**: + - `addLeaf(script, version)`: 添加叶子节点并对其哈希值进行字典序排序。 + - `getMerkleRoot()`: 构建 Merkle 树并返回根节点。 + - `buildTree(leaves)`: 递归构建 Merkle 树。 + +### Taproot地址计算 + +生成Taproot地址,实际上是对Tweaked 公钥的X坐标编码为 Bech32m 格式 +**流程** + +- 初始化 MAST 并添加叶子节点。 +- 构建 Merkle 树并计算根节点。 +- 生成内部公钥并计算 Tweaked 公钥。 +- 生成并输出 Taproot 地址。 + +```js +// 生成内部公钥 +const keyPair = bip32.fromSeed(crypto.randomBytes(32)); +const internalPubkey = keyPair.publicKey.slice(1, 33); // 移除 0x02 或 0x03 前缀 + +// 计算 Taproot 公钥 +const mastRoot = mast.getMerkleRoot(); +const { tweakedPubkey } = tapTweakPubkey(internalPubkey, mastRoot); + +// 生成 Taproot 地址 +const taprootAddress = payments.p2tr({ + internalPubkey: tweakedPubkey.slice(1, 33), + network: bitcoin.networks.bitcoin, +}).address; + +console.log('Taproot Address:', taprootAddress); +``` + +### 锁定脚本(scriptPubKey) + +对于隔离见证 Output,其 scriptPubKey 为 **OP_n tweaked-public-key** + +##### OP_n + +OP_n 表示隔离见证版本,版本 0 隔离见证 Output 的 scriptPubKey 的首个字节是 0x00,而版本 1 隔离见证 Output 的 scriptPubKey 的首个字节是 0x51 + +```js +OP_0: 0x00 // segwitV0 +OP_1: 0x51 // segwitV1,即Taproot +OP_2: 0x52 +... + +See: https://github.com/bitcoin/bitcoin/blob/v22.0/src/script/script.h#L68 +``` + +#### tweaked-public-key + +tweaked-public-key的计算比较复杂,有internal public key和script tree的Merkle Root组成,然后再进行Bech32m编码就能得到Taproot地址 + +##### script path + +script path是Taproot中比较灵活、同时比较复杂的一种方式 +tweaked-public-key的计算比较如上图:$$Q = P + t*G$$ + +#### key path + +key path不需要 Script Path,则可以去掉 Script 相关的哈希 +即:$$Q = P + t*G = P + TaggedHash('TapTweak', P)G$$ +钱包中的taproot地址就是基于**用户公钥做P**代入上方公式推导得到 + +#### **代码实现** + +```js +const mast = new MAST(); +mast.addLeaf(Buffer.from("OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG")); +mast.addLeaf(Buffer.from("OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG")); +mast.addLeaf(Buffer.from("OP_2 OP_2 OP_CHECKMULTISIG")); + +// 生成内部公钥 +const keyPair = bip32.fromSeed(crypto.randomBytes(32)); +const internalPubkey = keyPair.publicKey.slice(1, 33); // 移除 0x02 或 0x03 前缀 + +// 计算 Taproot 公钥 +const mastRoot = mast.getMerkleRoot(); +const { tweakedPubkey } = tapTweakPubkey(internalPubkey, mastRoot); + +// 获取 Tweaked 公钥的 X 坐标 +const tweakedPubkeyX = tweakedPubkey.slice(1, 33); + +// 生成锁定脚本(P2TR 地址的锁定脚本) +const lockingScript = bitcoin.script.compile([bitcoin.opcodes.OP_1, tweakedPubkeyX]); + +console.log('Locking Script:', lockingScript.toString('hex')); +``` + +### 解锁脚本(witnessScript) + +Taproot属于Segwit v1版本,其相关解锁脚本放在Witness位置 +如果在花费 P2TR UTXO 时,Witness 只包含一个元素,则是 P2TR (Key Path),如果在花费 P2TR UTXO 时,Witness 至少包含两个元素,则是 P2TR (Script Path)。在花费一个 P2TR UTXO 时,是通过 Witness 中元素的个数来决定使用 Key Path(Witness 元素个数为 1)还是 Script Path(Witness 元素个数大于等于 2)。 +在 Taproot 中,解锁script path可以使用 Taproot Tree 的任意路径之一来满足条件。 +下面代码以解锁一个script tree中的2-2多签叶子结点C为例: + +#### **代码实现** + +```js +class MAST { + // ... + // 获取叶子节点的路径 + getLeafPath(leaf) { + const index = this.leaves.indexOf(leaf); + if (index === -1) return null; + + let path = []; + let level = this.leaves.slice(); + + while (level.length > 1) { + const nextLevel = []; + for (let i = 0; i < level.length; i += 2) { + if (i + 1 < level.length) { + nextLevel.push(tapBranch(level[i], level[i + 1])); + } else { + nextLevel.push(level[i]); + } + if (i === index || i + 1 === index) { + path.push(i === index ? level[i + 1] : level[i]); + index = Math.floor(i / 2); + } + } + level = nextLevel; + } + return path; + } +} + +const mast = new MAST(); +const scriptA = Buffer.from("OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG"); +const scriptB = Buffer.from("OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG"); +const scriptC = Buffer.from("OP_2 OP_2 OP_CHECKMULTISIG"); +mast.addLeaf(scriptA); +mast.addLeaf(scriptB); +mast.addLeaf(scriptC); + +// 生成内部公钥 +const keyPairAlice = bip32.fromSeed(crypto.randomBytes(32)); +const keyPairBob = bip32.fromSeed(crypto.randomBytes(32)); +const internalPubkey = keyPairAlice.publicKey.slice(1, 33); // 移除 0x02 或 0x03 前缀 + +// 计算 Taproot 公钥 +const mastRoot = mast.getMerkleRoot(); +const { tweakedPubkey } = tapTweakPubkey(internalPubkey, mastRoot); + +// 获取 Tweaked 公钥的 X 坐标 +const tweakedPubkeyX = tweakedPubkey.slice(1, 33); + +// 构建交易以花费 Taproot 输出 +const txb = new bitcoin.TransactionBuilder(bitcoin.networks.bitcoin); +const value = 100000; // 假设输出金额为 100000 satoshis +const inputIndex = 0; // 输入索引 +const prevTx = bitcoin.Transaction.fromHex(''); // 上一个交易的对象 +const prevTxId = prevTx.getId(); // 上一个交易的 ID +const outputIndex = 0; // 上一个交易输出的索引 + +// 添加输入和输出 +txb.addInput(prevTxId, outputIndex); +txb.addOutput('', value - 1000); // 减去交易费用 + +// 假设要花费 scriptC +const leafToSpend = tapLeaf(0xc0, scriptC); +const leafPath = mast.getLeafPath(leafToSpend); + +// 构建 controlBlock +const controlBlock = Buffer.concat([ + Buffer.from([0xc0]), // 脚本版本 + internalPubkey, + ...leafPath, +]); + +// 签名交易 +const hashType = bitcoin.Transaction.SIGHASH_ALL; +const signatureHash = txb.buildIncomplete().hashForWitnessV1(inputIndex, [lockingScript], [value], hashType); +const signatureAlice = bitcoin.script.signature.encode(keyPairAlice.sign(signatureHash), hashType); +const signatureBob = bitcoin.script.signature.encode(keyPairBob.sign(signatureHash), hashType); + +// 构建解锁脚本(witness script) +const witnessScript = [signatureAlice, signatureBob, scriptC, controlBlock]; + +// 设置 witness +txb.setWitness(inputIndex, witnessScript); + +// 构建并输出交易 +const tx = txb.build(); +console.log('Transaction:', tx.toHex()); +``` + +**构建和签名交易:** + +- 创建一个交易以花费 Taproot 输出。 +- 添加输入和输出。 +- 获取要花费的脚本(例如 scriptC)的 TapLeaf 哈希值和 Merkle 路径。 +- 构建 controlBlock,包含脚本版本、内部公钥和 Merkle 路径。 +- 使用 Alice 和 Bob 的密钥分别对交易进行签名。 +- 构建包含两个签名和其他信息的解锁脚本(witness script)。 +- 设置交易的 witness。 + +#### script path + +[905ecdf95a84804b192f4dc221cfed4d77959b81ed66013a7e41a6e61e7ed530](https://blockchain.info/rawtx/905ecdf95a84804b192f4dc221cfed4d77959b81ed66013a7e41a6e61e7ed530)是花费 P2TR (Script Path) 的例子(它是一个 2-of-2 多签脚本),它的 Witness 为 + +```js +044123b1d4ff27b16af4b0fcb9672df671701a1a7f5a6bb7352b051f461edbc614aa6068b3e5313a174f90f3d95dc4e06f69bebd9cf5a3098fde034b01e69e8e788901400fd4a0d3f36a1f1074cb15838a48f572dc18d412d0f0f0fc1eeda9fa4820c942abb77e4d1a3c2b99ccf4ad29d9189e6e04a017fe611748464449f681bc38cf394420febe583fa77e49089f89b78fa8c116710715d6e40cc5f5a075ef1681550dd3c4ad20d0fa46cb883e940ac3dc5421f05b03859972639f51ed2eccbf3dc5a62e2e1b15ac41c02e44c9e47eaeb4bb313adecd11012dfad435cd72ce71f525329f24d75c5b9432774e148e9209baf3f1656a46986d5f38ddf4e20912c6ac28f48d6bf747469fb1 +``` + +根据编码我们得知有4个元素 + +```js +0070: .. .. .. .. .. .. 04 .. .. .. .. .. .. .. .. .. vin0 Witness Count: 4 +0070: .. .. .. .. .. .. .. 41 23 b1 d4 ff 27 b1 6a f4 vin0 Witness 0 Length:65 (0x41) +0080: b0 fc b9 67 2d f6 71 70 1a 1a 7f 5a 6b b7 35 2b +0090: 05 1f 46 1e db c6 14 aa 60 68 b3 e5 31 3a 17 4f +00a0: 90 f3 d9 5d c4 e0 6f 69 be bd 9c f5 a3 09 8f de +00b0: 03 4b 01 e6 9e 8e 78 89 01 .. .. .. .. .. .. .. +00b0: .. .. .. .. .. .. .. .. .. 40 0f d4 a0 d3 f3 6a vin0 Witness 1 Length:64 (0x40) +00c0: 1f 10 74 cb 15 83 8a 48 f5 72 dc 18 d4 12 d0 f0 +00d0: f0 fc 1e ed a9 fa 48 20 c9 42 ab b7 7e 4d 1a 3c +00e0: 2b 99 cc f4 ad 29 d9 18 9e 6e 04 a0 17 fe 61 17 +00f0: 48 46 44 49 f6 81 bc 38 cf 39 .. .. .. .. .. .. +00f0: .. .. .. .. .. .. .. .. .. .. 44 20 fe be 58 3f vin0 Witness 2 Length:68 (0x44) +0100: a7 7e 49 08 9f 89 b7 8f a8 c1 16 71 07 15 d6 e4 +0110: 0c c5 f5 a0 75 ef 16 81 55 0d d3 c4 ad 20 d0 fa +0120: 46 cb 88 3e 94 0a c3 dc 54 21 f0 5b 03 85 99 72 +0130: 63 9f 51 ed 2e cc bf 3d c5 a6 2e 2e 1b 15 ac .. +0130: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 41 vin0 Witness 3 Length:65 (0x41) +0140: c0 2e 44 c9 e4 7e ae b4 bb 31 3a de cd 11 01 2d +0150: fa d4 35 cd 72 ce 71 f5 25 32 9f 24 d7 5c 5b 94 +0160: 32 77 4e 14 8e 92 09 ba f3 f1 65 6a 46 98 6d 5f +0170: 38 dd f4 e2 09 12 c6 ac 28 f4 8d 6b f7 47 46 9f +0180: b1 +``` + +#### key path + +[dbef583962e13e365a2069d451937a6de3c2a86149dc6a4ac0d84ab450509c91](https://blockchain.info/rawtx/dbef583962e13e365a2069d451937a6de3c2a86149dc6a4ac0d84ab450509c91)是花费 P2TR (Key Path) 的例子,它的 witness 为: + +```js +044123b1d4ff27b16af4b0fcb9672df671701a1a7f5a6bb7352b051f461edbc614aa6068b3e5313a174f90f3d95dc4e06f69bebd9cf5a3098fde034b01e69e8e788901400fd4a0d3f36a1f1074cb15838a48f572dc18d412d0f0f0fc1eeda9fa4820c942abb77e4d1a3c2b99ccf4ad29d9189e6e04a017fe611748464449f681bc38cf394420febe583fa77e49089f89b78fa8c116710715d6e40cc5f5a075ef1681550dd3c4ad20d0fa46cb883e940ac3dc5421f05b03859972639f51ed2eccbf3dc5a62e2e1b15ac41c02e44c9e47eaeb4bb313adecd11012dfad435cd72ce71f525329f24d75c5b9432774e148e9209baf3f1656a46986d5f38ddf4e20912c6ac28f48d6bf747469fb1 +``` + +```js +0141e6e1fe41524e65e3040bc3d080a136345c2c806eb7f336dd6a7a79e9054b0d1fc6a8d836667ef6e9f2188cd1270ab28e5e0eb642eac89f2ec50a32ca54aaf9d601 + +01 .. .. .. .. .. .. .. .. .. .. .. .. .. .. vin0 Witness Count: 1 +.. 41 .. .. .. .. .. .. .. .. .. .. .. .. .. vin0 Witness 0 Length:65, schnorr_sig (64 bytes) + sig_hash (1 bytes) +.. .. e6 e1 fe 41 52 4e 65 e3 04 0b c3 d0 80 schnorr_sig +a1 36 34 5c 2c 80 6e b7 f3 36 dd 6a 7a 79 e9 +05 4b 0d 1f c6 a8 d8 36 66 7e f6 e9 f2 18 8c +d1 27 0a b2 8e 5e 0e b6 42 ea c8 9f 2e c5 0a +32 ca 54 aa f9 d6 +.. .. .. .. .. .. 01 sig_hash: SIGHASH_ALL (0x01) +``` diff --git a/BTC/Advanced/Taproot/README.md b/BTC/Advanced/Taproot/README.md new file mode 100644 index 000000000..8c2d09f77 --- /dev/null +++ b/BTC/Advanced/Taproot/README.md @@ -0,0 +1,72 @@ +### Taproot的详细历史和特点 + +#### 起源与发展 + +1. **提案的提出**: + - Taproot的设计始于比特币改进提案 [BIP 340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki)、[BIP 341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)和[BIP 342](https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki)。BIP 340介绍了Schnorr签名算法在比特币中的应用,BIP 341提出了Taproot的概念,而BIP 342则探讨了Taproot和Schnorr的集成。 + +2. **设计目标**: + - Taproot的主要设计目标包括提高比特币网络的隐私性、可扩展性和支持更复杂的智能合约。通过引入Schnorr签名和Merkle树结构,Taproot旨在优化多重签名交易的外观,并将其与常规单签名交易统一化,从而提升隐私保护。 + +#### 技术特点 + +1. **Schnorr签名和签名聚合**: + - Taproot引入了Schnorr签名算法,这种签名算法相较于比特币当前使用的ECDSA签名更高效,并支持多个输入的签名聚合。这意味着多重签名交易的签名可以被聚合成一个单一的签名,显著减小了交易大小,降低了交易费用,并提高了链上交易的隐私性。 + +2. **Merkle树结构**: + - Taproot利用Merkle树结构,将多种不同的比特币脚本路径合并成一个单一的“扩展公钥”。这使得在链上查看的所有交易看起来都像是来自一个单一的地址,从而隐藏了实际执行的交易路径和使用的脚本类型,进一步提升了隐私性。 + +3. **智能合约支持**: + - Taproot通过其灵活的脚本能力,支持更复杂和高级的智能合约。这些合约可以以更简洁和隐私的方式实现,允许比特币用户创建更复杂的交易逻辑和条件,如多重签名、时间锁定、条件支付等。 + +#### 部署和实施 + +1. **社区支持和开发**: + - Taproot的开发经历了长时间的讨论和审查,由比特币社区内的核心开发者、研究人员和网络参与者共同推动。通过开放的讨论和测试网络(如Bitcoin Testnet),确保了升级的安全性和兼容性。 + +2. **激活过程**: + - Taproot于2021年11月通过比特币矿工社区的投票得到了广泛支持和认可。随后,升级计划在2022年11月在比特币主网上正式生效,这意味着所有比特币节点都支持Taproot升级,并能够开始使用新的Taproot功能。 + +### Segwit版本区别 + +#### Segwit 版本0(P2WPKH和P2WSH) + +##### 特点: +1. **P2WPKH(Pay to Witness Public Key Hash)**:这种类型的地址通常以`bc1q`开头。 +2. **P2WSH(Pay to Witness Script Hash)**:这种类型的地址通常以`bc1q`开头,用于更复杂的多重签名(multisig)脚本。 +3. **隔离见证**:交易的见证数据被隔离到一个独立的区块部分,减小了交易数据的大小,从而提高了区块的容量。 +4. **交易格式**:新的交易格式,包含一个见证字段,用于存储签名等见证数据。 +5. **可延展性问题的解决**:通过隔离见证,解决了交易的可延展性问题,使得交易ID在没有更改其内容的情况下不能被篡改。 + +#### Segwit 版本1(Taproot) + +##### 特点: +1. **Taproot**:这是Segwit版本1的主要改进,它是一个更复杂的地址方案,通常以`bc1p`开头。 +2. **Schnorr签名**:引入了Schnorr签名算法,这种算法比原来的ECDSA签名更高效和安全,可以实现批量验证签名,从而进一步提高效率。 +3. **Mast(Merkelized Abstract Syntax Tree)**:允许更复杂的智能合约和多重签名方案,只有在执行时需要的部分才会被暴露,增强了隐私性和效率。 +4. **更高的灵活性和隐私**:与传统的多重签名方案相比,Taproot允许在不暴露具体条件的情况下进行复杂的条件支付,大大增强了隐私性。 +5. **兼容性**:保持与Segwit版本0的兼容性,新的功能和特性在使用时才会生效。 + +#### 功能对比 + +| 特点 | Segwit 版本0 | Segwit 版本1 (Taproot) | +|-------------------------------|------------------------------------|---------------------------------| +| **地址格式** | `bc1q` 开头 | `bc1p` 开头 | +| **主要改进** | 隔离见证(Segregated Witness) | Taproot | +| **签名算法** | ECDSA 签名 | Schnorr 签名 | +| **交易格式** | 包含见证字段,分离见证数据 | 与版本0兼容,增加了新的见证字段 | +| **隐私和效率** | 提高区块容量,解决交易可延展性问题 | 进一步提高隐私性和效率 | +| **智能合约功能** | 基础单签名和简单多重签名方案 | 支持更复杂的智能合约和条件支付 | +| **Mast 支持** | 不支持 | 支持(Merkelized Abstract Syntax Tree) | +| **批量签名验证** | 不支持 | 支持(Schnorr签名的特性) | +| **兼容性** | 不适用于Taproot | 兼容Segwit版本0 | + +#### 锁定脚本和解锁脚本的对比 +| Type | scriptPubKey(锁定时使用) | Witness(花费时使用) | 说明 | +|-------------------------|------------------------------------------------|----------------------------------------------|-----------------------------------------| +| **P2WPKH** | 0x0014{20-byte-key-hash} | \ \ | 支付到单一公钥哈希,常用于单签名交易。 | +| **P2WSH** | 0x0020{32-byte-hash} | \ \ | 支付到复杂脚本哈希,适用于多重签名或其他复杂脚本。| +| **P2TR (Key Path)** | 0x5120{32-byte-tweaked-public-key} | \ | 使用Taproot支付到单一公钥,简化的单签名。 | +| **P2TR (Script Path)** | 0x5120{32-byte-tweaked-public-key} | \ \ \ | 使用Taproot支付到复杂脚本路径,支持复杂的条件支付。| + +Taproot的地址计算、锁定脚本和解锁脚本详见[MAST](./MAST.md) \ No newline at end of file diff --git a/BTC/Application/ARC-20/README.md b/BTC/Application/ARC-20/README.md new file mode 100644 index 000000000..9efa14408 --- /dev/null +++ b/BTC/Application/ARC-20/README.md @@ -0,0 +1,75 @@ +# ARC-20 简介 +## Atomical +Atomicals 协议是一种简单而灵活的协议,用于为比特币等未花费的交易输出 (UTXO) 区块链铸造、传输和更新数字对象 (传统上称为不可替代代币)。原子(或“原子”)是一种组织数字对象的创建、传输和更新的方式——它本质上是根据一些简单规则定义的数字所有权链。 +### Atomicals 与其他流行的不可替代代币 (NFT) 协议进行比较。 +| 功能 | Atomicals | Ordinals | Ethereum ERC721 | +|---------------------|------------------------------------------------|--------------------------------------------------|--------------------------------------------| +| 价值主张 | 数字对象、数字工件、数字收藏品 | 数字对象、数字工件、数字收藏品 | 数字对象、数字工件、数字收藏品 | +| 区块链 | 比特币及所有 UTXO 区块链 | 比特币及所有 UTXO 区块链 | 兼容以太坊 EVM 的区块链 | +| 铸币技术 | 比特币:提交和显示,使用 "atom" 封套 | 比特币:提交和显示,使用 "ord" 封套 | 资金和部署合约账户 | +| 数据存储 | 在铸造时存储一个或多个文件 | 在铸造时仅存储一个文件 | 未定义 | +| 动态状态 | 定义和更新基本和任何复杂文件类型的应用状态 | 未定义 - 可以基于应用特定来定义 | 未定义 - 可以预先编程 | +| 验证 | 目前通过索引服务 "electrumx" 验证 - 理论上可以客户端全部验证 | 目前通过索引服务 "ord" 验证 - 理论上可以客户端全部验证 | 信任以太坊 EVM 节点或实际上信任 Metamask 或 Infura | +| 索引 | 目前依赖于 elecrumx atomicals 索引器 (Python) 进行追踪序号系统 | 目前依赖于 ord 索引器 (Rust) 进行追踪序号系统 | 使用原生以太坊 EVM 节点或实际上信任 Metamask 或 Infura | +| 地址格式 | 铸造和更新需要 P2TR (Taproot) 地址 | 所有使用如铸造和转移都需要 P2TR (Taproot) 地址 | 使用原生以太坊账户地址 | +| 集合 | 首次的 "容器" NFT 用于更新集合,可以永久封闭 | 未定义但正在进展中的父子关系 | 定义为单独的 ERC | +| 原子交换 | 部分签名比特币交易 (PBSTs) | 部分签名比特币交易 (PBSTs) | 定义为单独的 ERC | +| 可替代代币 | 首次 "ARC20" 使用 Satoshi 作为计量单位。去中心化和直接铸造模式可用。内置 Ticker 名称服务 | 基本协议中未提供。"BRC20" 通过铸造在铭文中创建 JSON 协议来定义去中心化铸造规则和 Ticker 名称。 | 定义为 ERC20 类型代币 | +| 域名服务 | Realms 作为首次的 NFT,代表域名和数字身份。一种新的无域名后缀的命名标准,以加号 "+" 开头,如 +username | 基本协议中未提供。".names" 和 ".sats" 协议作为铭文中的 JSON 协议存在,定义声明和更新规则。 | 现有服务如 ENS 和 Unstoppable Domains | + + + +## ARC-20 +Atomicals 协议解决了如何在比特币区块链上表示任意可替代代币资产的长期存在的问题。ARC-20 可替代代币标准最终为比特币带来了彩色硬币,并使用每个 Satoshi 来代表已部署代币的所有权单位。这意味着每个单位的代币永远由 1 聪支持。它充当一种支撑代币价值的“数字黄金含量”。这也意味着根据定义,每个代币的价值永远不会低于 1 聪。 + +ARC-20 使用原生 Satoshi 单位来表示每个代币,它们可以像普通比特币一样拆分和组合。ARC-20 代币可以由任何人铸造,并转移到任何比特币地址类型,并可与支持 UTXO 选择的钱包配合使用,例如 Sparrow Wallet (外部链接)。有两种部署模式:直接部署和分散部署。下面详细了解 ARC-20 的不同铸造方式及其优点。 +![dune](./images/atomical.png) +据 Dune 链上数据统计,截止2023 年 3 月 6 日, 巅峰期 Atomical 交易占比超过铭文市场交易的六成之多,是继Ordinal之外BTC生态采用度最高的协议。 + +## 协议详情 +Atomicals 协议使用原子交换的方案进行 Atomical 的铸造,并在交易输出环节融入了 Commit-Reveal 方案,以此来确保用户铸造 Atomical 过程的不可篡改性和隐私性。此外,它还在铸造过程中添加了唯一标识符来为 Atomical 的安全性和可追溯性提供保障。 + +### Deploy +去中心化部署是通过ticker、每铸币奖励、允许的总铸币数、起始块高度和元数据来初始化的 + +| 参数名 | 描述 | +|-------------|------------------------------------------------------| +| tick | 全局唯一的代币名称(ticker name) | +| per_mint_amt| 每次成功铸造时授予的代币数量。以 Satoshi 为单位。 | +| mint_count | 在额度耗尽之前允许的铸造总次数,达到后将成为“完全铸造”状态。 | +| start_height| 可以开始铸造的起始区块高度。可以设置为 0 或任何未来的区块高度。 | +| image | 代表代币的图像图标。文件名将出现在代币中。在引入文件时使用 "image.jpg" 或 "image.png" 并请谨慎重命名。| + + +### Mint +```js +npm run cli mint-dft + +Optional flags: +--satsbyte= +``` +去中心化的铸币功能mint-dft允许从start_height区块高度开始铸造tick + +所需参数: + +tick:全全局唯一的代币名称(ticker name) + +可选标志: + +--satsbyte=<数字> + +设置交易的每字节聪并覆盖默认值,用来加速 + +### Transform + +ARC20 代币传输的正常操作是所有输入值的总和应完全或干净地分配到可用输出中。如果没有足够的输出值,或者后续输出之一会导致过度分配(即:代币单位的通货膨胀),则剩余余额将被永久销毁或烧毁。 + +#### 完全分配 +使用 FIFO 规则将所有输入干净地分配给输出。 +原子 1932、7920、0542 是不可替代代币 (NFT),按先进先出顺序分配,同时跳过不可花费的输出 (OP_RETURN)。第一个输入 NFT 从输出 0 开始分配,然后每个后续 NFT 输入分配给下一个可用输出。同一 UTXO 输入中的多个 Atomical 按atomical_id 排序。由于“1932”在“7920”之前,因此它排在第一位。 +![alt text](./images/transfer1.png) + +#### 未完全分配 +在无法完全(或“干净”)分配输出的情况下,算法会尝试从输出 0 开始分配所有 ARC20 令牌,无论是否已分配另一个 ARC20。做出这一决定是为了在开发人员或钱包意外未能跟踪多个输入 ARC20 的情况下给予宽大处理 - 换句话说,将“尽最大努力”尝试进行补偿和分配。 +原子 1932、7920、0542 是不可替代代币 (NFT),按先进先出顺序分配,同时跳过不可花费的输出 (OP_RETURN)。如果它们无法“干净地分配”,如上面的示例中,第 5 个输出有 700 个单位(这对于 Atomical 0542 的 620 值来说太多了),则算法默认为“尽力而为”,从输出开始分配所有 ARC20 0。 +![alt text](./images/transfer2.png) \ No newline at end of file diff --git a/BTC/Application/ARC-20/images/atomical.png b/BTC/Application/ARC-20/images/atomical.png new file mode 100644 index 000000000..2386fc910 Binary files /dev/null and b/BTC/Application/ARC-20/images/atomical.png differ diff --git a/BTC/Application/ARC-20/images/transfer1.png b/BTC/Application/ARC-20/images/transfer1.png new file mode 100644 index 000000000..63d8da6c2 Binary files /dev/null and b/BTC/Application/ARC-20/images/transfer1.png differ diff --git a/BTC/Application/ARC-20/images/transfer2.png b/BTC/Application/ARC-20/images/transfer2.png new file mode 100644 index 000000000..7702330b8 Binary files /dev/null and b/BTC/Application/ARC-20/images/transfer2.png differ diff --git a/BTC/Application/BRC-20/README.md b/BTC/Application/BRC-20/README.md new file mode 100644 index 000000000..b5aa9e7ad --- /dev/null +++ b/BTC/Application/BRC-20/README.md @@ -0,0 +1,43 @@ +# BRC-20 简介 +## Ordinals +Ordinals是Satoshi比特币的最小单位,同下文聪)的一种编号方案,允许跟踪和传输单个聪。这些数字称为序数。聪按照开采的顺序进行编号,并以先进先出的方式从交易输入转移到交易输出。编号方案和转移方案都依赖于 order,编号方案取决于开采聪的顺序,转移方案取决于交易输入和输出的顺序。Ordinals协议允许在比特币的单个聪上“铸造”或附加数据(称为铭文),这意味着每个聪可以成为一个独特的、非同质化的资产。同时基于聪上携带的铭文我们可以发行同质化资产和非同质资产类似于以太坊上的ERC20和NFT,而 BRC-20 就是 Ordinals 协议知名度最高的资产发行方案。 + +## BRC-20 +2023 年 3 月 11 日,domo在Twitter上发起了一项实验,该实验基于Ordinal协议,并定义了一种新的Token标准:“BRC-20”。这个实验性的协议专注于在比特币网络上资产的铸造和转移,意外地打开了潘多拉魔盒,激发了人们多年来对于在比特币网络上发行资产的强烈兴趣。 +![dune](./images/dune.jpg) +据 Dune 链上数据统计,截止2023 年 12 月 3 日, 巅峰期 BRC-20 交易占比超过比特币网络交易的六成之多,是迄今为止BTC生态采用度最高的协议。Ordinal和BRC-20协议从技术层面来说并不是一个优秀的技术,但其是去掉智能合约的资产发行足够简单以及Fair Mint的特点,让用户更加容易参与进来。 +这次实验不仅点燃了市场的热情,还引发了一场广泛的“铭文潮流”,也就是在比特币网络上铸造和交易各种Token的潮流。接着,类似于BRC-20的铭文协议如雨后春笋般涌现,不仅在比特币网络上蓬勃发展,还迅速扩散到其他公链平台。这次实验中的一种Token——ordi,已经取得了显著的市场成就。截至目前,ordi的市值已经超过了10亿美元,成为了加密资产市场中的一颗新星,稳居市值排行榜前50位。这个成就不仅证明了BRC-20和类似协议的潜力,也显示了区块链技术在资产Token化领域的巨大应用前景。 +总的来说,domo的这次实验不仅是一个技术上的突破,也是一个市场和社区层面的重大事件。它不仅为比特币网络上的资产发行开辟了新天地,还为整个加密资产生态系统的发展注入了新的活力和创新。 +![brc20-experiment](./images/brc20-experiment.jpg) + +## 协议详情 +### Deploy +```js +{"p":"brc-20","op":"deploy","tick":"ordi","max":"21000000","lim":"1000"} +``` +| Key | Required? | Description | +|------|-----------|-----------------------------------------------------------------------------------------------| +| p | Yes | Protocol: Helps other systems identify and process BRC-20 events | +| op | Yes | Operation: Type of event (Deploy, Mint, Transfer) | +| tick | Yes | Ticker: 4 letter identifier of the BRC-20 | +| max | Yes | Max supply: set max supply of the BRC-20 | +| lim | No | Mint limit: If letting users mint to themselves, limit per ordinal (unisat not validate) | +| dec | No | Decimals: set decimal precision, default to 18 | + +### Mint +```js +{"p":"brc-20","op":"mint","tick":"ordi","amt":"100000"} +``` +| Key | Required? | Description | +|------|-----------|-------------------------------------------------------------------| +| p | Yes | Protocol: Helps other systems identify and process brc-20 events | +| op | Yes | Operation: Type of event (Deploy, Mint, Transfer) | +| tick | Yes | Ticker: 4 letter identifier of the brc-20 | +| amt | Yes | Amount to mint: States the amount of the brc-20 to mint. Has to be less than "lim" above if stated | + +### Transform +- 铸造transfer铭文 +```js +{"p":"brc-20","op":"transfer","tick":"ordi","amt":"10"} +``` +- 将transfer铭文通过PSBT的方法转移给receiver \ No newline at end of file diff --git a/BTC/Application/BRC-20/images/brc20-experiment.jpg b/BTC/Application/BRC-20/images/brc20-experiment.jpg new file mode 100644 index 000000000..d93b85739 Binary files /dev/null and b/BTC/Application/BRC-20/images/brc20-experiment.jpg differ diff --git a/BTC/Application/BRC-20/images/dune.jpg b/BTC/Application/BRC-20/images/dune.jpg new file mode 100644 index 000000000..b3717ba9f Binary files /dev/null and b/BTC/Application/BRC-20/images/dune.jpg differ diff --git a/BTC/Application/Runes/README.md b/BTC/Application/Runes/README.md new file mode 100644 index 000000000..a95c6e61e --- /dev/null +++ b/BTC/Application/Runes/README.md @@ -0,0 +1,104 @@ +# 背景 +## Ordinals +Ordinals 最早由 Casey (@rodarmor) 在2022年12月发布,该协议允许在比特币的最小单位聪上铭刻数据,包含文本、图片、音频、视频等数据信息,进而把NFT引入到比特币生态。而我们一直说的比特币铭文也是一段通过采用Ordinals 协议铭刻(Inscribe)在聪(Satoshis)上的元数据。 + +## BRC-20 +BRC-20 是由匿名开发者 @domodata 于2023年3月8日推出,是基于刚介绍的 Ordinals协议推出的协议。该协议通过将代币的名称、数量等信息用特定的 JSON 格式写入聪(Satoshi)中以实现代币的部署Deploy、铸造Mint和转账Transfer的功能。仿照以太坊 ERC-20 代币标准使用 BRC-20 命名,相当于基于 Ordinals 协议的代币发行协议。详细介绍参考 [BRC-20 协议](../BRC-20/README.md)。 + +## Runes +2023年9月1日 Casey 公开反对BRC20协议,并希望可以停止铸造 BRC-20。在9月26日,Casey Rodarmor 重新开发了一个名为 Runes 的协议作为 BRC-20 的替代品。该协议是一个基于 UTXO 的、能使比特币使用者具有使用良好体验的可替代代币协议。Casey 认为改良过后,能够降低制造大量垃圾 UTXO 现象的符文,是比先前实验性的 BRC-20 协议更好、更轻量简洁的资产发行方案。至少目前 BRC -20 的流行已经创造了大量的“垃圾”UTXO 。 + + +# Runes协议介绍 +符文协议允许比特币交易蚀刻、铸造和转移比特币原生数字商品。 + +## 符文石(RuneStone) +> 符文协议消息,称为符文石,存储在比特币交易输出中。 在区块 840,000(比特币第四次减半) 上激活, 早期区块中的符文石将被忽略。 + +符文石输出的脚本以`OP_RETURN`开头,然后是 `OP_13`,最后是零个或多个数据推送。这些推送的数据被串联起来并解码为128位整数序列,最后解析为符石,相关编解码逻辑见[ULEB128](./ULEB128.md)。 +一笔交易最多可以拥有一颗符文石。符文石可以蚀刻一个新的符文或者铸造一个现有的符文,并将符文从交易的输入传输到其输出。 +```js +struct Runestone { + edicts: Vec, + etching: Option, + mint: Option, + pointer: Option, +} +``` +- `edicts`:标识符文的转移信息,用于符文转账。 +- `etching`:标识符文的蚀刻信息,通过蚀刻创建新符文。 +- `mint`:存储符文 ID,用于符文铸造。 +- `pointer`:标识未分配的符文的默认转移位置,不设置则将剩余的未分配符文将转移到交易的第一个非OP_RETURN输出。 + +### 符文(Rune) +符文由 ID 标识,ID 包含蚀刻符文的区块以及该区块内蚀刻交易的索引,以文本形式表示为BLOCK:TX。例如,第 500 个区块的第 20 笔交易中铸造的符文 ID 为500:20。 + +### 蚀刻(Etching) +符文石中包含的蚀刻信息用于创建新符文 +```js +struct Etching { + divisibility: Option, + premine: Option, + rune: Option, + spacers: Option, + symbol: Option, + terms: Option, +} + +struct Rune(u128); + +struct Terms { + amount: Option, + cap: Option, + height: (Option, Option), + offset: (Option, Option), +} +``` +- `divisibility`:符文精度,标识符文数量中小数点后允许的位数。 +- `premine`:为自己分配被蚀刻的符文数量,用于预挖。 +- `rune`:符文名称,由字母 A 到 Z 组成,例如UNCOMMONGOODS。 +- `spacers`:符文分隔符,位域的第 N 个字段(从最低有效位开始)确定是否应在从符文名称左侧开始的第 N 个和第 N+1 个字符之间显示间隔符,举例如下 + +| Spacers | Display | +|---------|----------| +| 0b1 | A•AAA | +| 0b11 | A•A•AA | +| 0b10 | AA•AA | +| 0b111 | A•A•A•A | + +- `symbol`:符文货币符号的 Unicode 代码点,显示在该符文的金额之后。如果符文没有货币符号,¤则应使用通用货币字符。 +- `terms`:符文条款,铸币时需要满足的条件。 + - `amount`: 每个铸币交易收到的符文数量 + - `cap`: 允许的铸造次数 + - `height`: 铸造的起始和结束绝对区块高度 + - `offset`: 相对于符文被蚀刻的块的起始和结束块高度 + +### 铸造 +符文石中包含的mint信息用于铸造符文,铸币数量将添加到交易输入中未分配的符文中。这些符文可以使用Edict进行转移,否则将转移到第一个非OP_RETURN输出或由字段指定的输出Pointer。 + +### 转账 +符文石中包含的edict信息用于转移符文,符文石可以包含任意数量的法令,这些edict是按顺序处理的。 +```js +struct Edict { + id: RuneId, + amount: u128, + output: u32, +} +``` +- id:符文id。 +- amount:符文转移数量。 +- output:符文输出位置,对应vout的下标。 + +转移规则: +1. 优先处理符文石的edict,将输入中对应的符文转移到输出的output位置的UTXO上 +2. 如果有剩余的符文则会均匀分配到输出上 +3. 如果未分配的符文数量不能被非OP_RETURN输出的数量整除,多余的符文会分配给第一个非OP_RETURN的输出 + +如果符石中的任何edict的符文ID的block=0且tx>0,或者output大于交易输出的数量,则该符石是纪念碑。纪念碑中的法令不会被处理,所有输入的符文都会被烧毁。 + +## 纪念碑(Cenotaphs) +符石可能因多种原因而格式错误,包括符石中的非推送数据操作码OP_RETURN、无效的变体或无法识别的符石字段。畸形的符文石被称为**纪念碑**。 + +输入到纪念碑交易中的符文会被烧毁。在带有纪念碑的交易中蚀刻的符文被设置为不可铸造。带有纪念碑的交易中的铸币计入铸币上限,但铸造的符文会被烧毁。 + +纪念碑是一种升级机制,允许符文被赋予新的语义,从而改变符文的创建和传输方式,同时不会误导未升级的客户端这些符文的位置,因为未升级的客户端会看到这些符文已被烧毁。 \ No newline at end of file diff --git a/BTC/Application/Runes/ULEB128.md b/BTC/Application/Runes/ULEB128.md new file mode 100644 index 000000000..745c87556 --- /dev/null +++ b/BTC/Application/Runes/ULEB128.md @@ -0,0 +1,247 @@ +# 简介 + +ULEB128(Unsigned Little Endian Base 128)是一种编码方式,设计用于高效地表示无符号整数,尤其是在计算机科学和编程中。它采用变长编码方式,旨在减少存储和传输数据时的开销。 + +# 示例 + +## ULEB128 编码 + +ULEB128编码将无符号整数编码为一系列字节,其中每个字节除了最后一个字节外,最高位都被设置为1,表示还有更多的字节。 +整数从其最低有效位到最高有效位进行编码,逐字节处理,每次处理7位。处理的过程中,将7位数据放入字节的低7位,若还有更多数据待处理,则设置高位为1,否则设置为0。这种编码方式保证了较小的数值占用较少的空间。 +** 举例 ** +假设要编码整数 300(二进制表示为 10, 0101100): + +- 第一个字节中,低7位为 0101100(十进制的 44),最高位设置为 1(表示还有数据),因此第一个字节为 10101100。 +- 第二个字节仅包含剩余的 10,放在最低位,其余位填充为0,最高位为 0(表示这是最后一个字节),因此第二个字节为 00000010。 +- 最终编码为 0xD4 0x02。 + +## ULEB128 解码 + +ULEB128 解码需要观察每个字节的最高位(也称为继续位),在 ULEB128 编码中,每个字节的最高位(第8位)用于指示是否还有更多的字节需要读取。 + +- 如果这一位是 1,表示后续还有至少一个字节是这个数的一部分。 +- 如果这一位是 0,则表示这是最后一个字节。 + ** 举例 ** + 假设要解码十六进制 **cfa433** +- 第一个字节 cf(二进制:11001111),数据位:1001111(二进制),十进制值为 79。 +- 第二个字节 a4(二进制:10100100),数据位:0100100(二进制),十进制值为 36。 +- 第三个字节 33(二进制:00110011),数据位:0110011(二进制),十进制值为 51。 + +相加得到 79 + 36 _ 128 + 51 _ 128^2 = 840271 + +## 在Runes中应用 + +## 蚀刻 + +### 数据结构 + +```js +type Etching = { + divisibility?: U8; + premine?: U128; + rune?: Rune; + spacers?: U32; + symbol?: Symbol; + terms?: Terms; +}; + +type Rune = u128; + +type Terms = { + amount?: U128; + cap?: U128; + height?: { + start?: U64; + end?: U64; + }; + offset?: { + start?: U64; + end?: U64; + }; +}; +``` + +### 示例 + +| DOG•GO•TO•THE•MOON 蚀刻交易 +https://mempool.space/zh/tx/e79134080a83fe3e0e06ed6990c5a9b63b362313341745707a2bff7d788a1375 + +**1. Raw OP_RETURN** + +```js +OP_RETURN +OP_PUSHNUM_13 +OP_PUSHBYTES_33 02010487a1c3f0c0ebf7fb9d01010503d4040595e80706808084fea6dee1111601 +``` + +**2. ULEB128解码** + +```js +[ + { decimal: 2n, hex: '02' }, + { decimal: 1n, hex: '01' }, + { decimal: 4n, hex: '04' }, + { decimal: 11382812169668186247n, hex: '87a1c3f0c0ebf7fb9d01' }, + { decimal: 1n, hex: '01' }, + { decimal: 5n, hex: '05' }, + { decimal: 3n, hex: '03' }, + { decimal: 596n, hex: 'd404' }, + { decimal: 5n, hex: '05' }, + { decimal: 128021n, hex: '95e807' }, + { decimal: 6n, hex: '06' }, + { decimal: 10000000000000000n, hex: '808084fea6dee111' }, + { decimal: 22n, hex: '16' }, + { decimal: 1n, hex: '01' }, +]; +``` + +**3. 对照码表翻译** + +```js +{ + "edicts": [], + "etching": { + "divisibility": "5", + "premine": "10000000000000000", + "rune": "11382812169668186247", + "spacers": "596", + "symbol": "128021", + "turbo": false, + "terms": null + }, + "mint": null, + "pointer": "1", + "flags": "1", +} +``` + +## 铸造 + +### 数据结构 + +```js +type Mint = { + block: U64; + tx: U32; +} +``` + +### 示例 + +| GOLD•RUNE•STONE 铸造交易 +https://mempool.space/zh/tx/b4da683f34fa2a159f887012a67a14e56dbbf8e38e2da991be45869413544e0b + +**1. Raw OP_RETURN** + +```js +OP_RETURN +OP_PUSHNUM_13 +OP_PUSHBYTES_16 14cfa43314b80400cfa433b804e80700 +``` + +**2. ULEB128解码** + +```js +[ + { decimal: 20n, hex: '14' }, + { decimal: 840271n, hex: 'cfa433' }, + { decimal: 20n, hex: '14' }, + { decimal: 568n, hex: 'b804' }, + { decimal: 0n, hex: '00' }, + { decimal: 840271n, hex: 'cfa433' }, + { decimal: 568n, hex: 'b804' }, + { decimal: 1000n, hex: 'e807' }, + { decimal: 0n, hex: '00' }, +]; +``` + +**3. 对照码表翻译** + +```js +{ + "edicts": [ + { + "id": { + "block": "840271", + "tx": "568" + }, + "amount": "1000", + "output": "0" + } + ], + "etching": null, + "mint": { + "block": "840271", + "tx": "568" + }, + "pointer": null +} +``` + +## 转账 + +### 数据结构 + +```js +type Mint = { + block: U64; + tx: U32; +} +``` + +### 示例 + +| DOG•GO•TO•THE•MOON 转移交易 +https://mempool.space/zh/tx/966aff320562acd491458bded8fc9010590285aa3abd4801fe06192c242544ee + +**1. Raw OP_RETURN** + +```js +OP_RETURN +OP_PUSHNUM_13 +OP_PUSHBYTES_21 160b00c0a2330380ea8ed51f0b00008094ebdc030d +``` + +**2. ULEB128解码** + +```js +[ + { decimal: 22n, hex: '16' }, + { decimal: 11n, hex: '0b' }, + { decimal: 0n, hex: '00' }, + { decimal: 840000n, hex: 'c0a233' }, + { decimal: 3n, hex: '03' }, + { decimal: 8500000000n, hex: '80ea8ed51f' }, + { decimal: 11n, hex: '0b' }, + { decimal: 0n, hex: '00' }, + { decimal: 0n, hex: '00' }, + { decimal: 1000000000n, hex: '8094ebdc03' }, + { decimal: 13n, hex: '0d' }, +]; +``` + +**3. 对照码表翻译** + +```js +{ + "edicts": [ + { + "id": { + "block": "840000", + "tx": "3" + }, + "amount": "8500000000", + "output": "11" + }, + { + "id": { + "block": "840000", + "tx": "3" + }, + "amount": "1000000000", + "output": "13" + } + ], + "pointer": "11" +} +``` diff --git a/BTC/Basic/explorer/README.md b/BTC/Basic/explorer/README.md new file mode 100644 index 000000000..0eb90ad33 --- /dev/null +++ b/BTC/Basic/explorer/README.md @@ -0,0 +1,371 @@ +概述 + +区块链浏览器是一种用于查看和检索区块链上数据的在线工具,类似于传统互联网中的搜索引擎。它并非传统意义上的“浏览器”(如Chrome或Safari),而是专门为区块链设计的透明化数据查询工具,用于查询和展示区块链网络中的公开数据。其核心功能是解析并可视化分布式账本中的信息,例如交易记录、区块详情、地址余额、智能合约状态等。用户通过区块链浏览器可以实时监控链上活动,验证交易真实性,并分析网络运行状态。 + +以下是其要点: + +1.技术原理 + +1.1 区块链数据结构解析 + +区块链的本质是一个按时间顺序连接的链式数据结构,每个区块包含以下核心元素: + +-区块头(Block Header):存储区块的元数据,包括版本号、时间戳、前序区块哈希(Previous Hash)、默克尔根(Merkle Root)、难度值等 + +-交易列表(Transactions):记录该区块打包的所有交易信息,例如发送方、接收方、金额、Gas费用等 + +-哈希值(Block Hash):通过加密算法(如SHA-256)生成的唯一标识符,用于确保区块数据的不可篡改性 + +-区块链浏览器通过解析这些数据结构,将原始二进制或十六进制数据转换为可读的格式(如JSON或HTML),并在前端界面中展示 + + +1.2 节点交互与数据同步 + +区块链浏览器需要与网络中的节点(Node)建立连接以获取实时数据。节点分为全节点和轻节点: + +-全节点:存储完整的区块链数据,能够独立验证交易和区块的有效性 + +-轻节点:仅存储区块头信息,依赖其他节点提供交易细节 + +浏览器通常通过节点的RPC(Remote Procedure Call)接口或专用API获取数据。例如,以太坊的Web3.js库允许开发者与以太坊节点通信,查询账户余额或交易详情 + + +1.3 数据索引与存储优化 + +由于区块链数据量庞大(如比特币区块链已超过400GB),直接查询链上数据效率低下。因此,区块链浏览器通常采用以下优化技术: + +-建立索引数据库:将区块高度、交易哈希、地址等关键字段存入关系型数据库(如MySQL)或NoSQL数据库(如MongoDB),加速查询响应 + +-缓存机制:对高频访问的数据(如最新区块)进行缓存,减少重复计算 + +-分布式架构:采用微服务设计,将数据采集、处理和展示模块分离,提升系统扩展性 + + +1.4 前端与后端技术栈 + +-前端:使用React、Vue等框架构建交互式界面,结合图表库(如D3.js)展示交易趋势、网络算力等统计信息 + +-后端:采用Node.js、Python或Go语言开发API服务,处理数据查询请求并与区块链节点交互 + +-安全机制:通过HTTPS加密传输、防止SQL注入和跨站脚本(XSS)攻击,确保用户数据安全 + + + +2.核心功能 + +区块链浏览器的功能设计围绕链上数据的透明性和可追溯性展开,以下是其核心功能模块: + +2.1 区块与交易查询 + +-区块详情:展示区块高度、哈希值、时间戳、矿工地址、交易数量、Gas消耗等信息 + +-交易追踪:通过交易哈希(TxHash)查询交易状态(确认数)、输入输出地址、转账金额及手续费 + +-地址监控:查看特定地址的余额、交易历史记录及关联的智能合约 + + +2.2 实时数据展示 + +-最新区块列表:滚动显示最新生成的区块及其基础信息 + +-未确认交易池(Mempool):展示待打包交易的排队情况,帮助用户预估交易确认时间 + + +2.3 统计分析与可视化 + +-网络算力(Hash Rate):实时显示全网挖矿算力分布及变化趋势 + +-交易吞吐量:统计每秒交易数(TPS)及区块间隔时间 + +-代币分布:分析通证(Token)的持有者地址数量及持仓比例 + + +2.4 智能合约交互 + +-合约代码审计:展示智能合约的源代码及字节码,支持开发者验证合约逻辑 + +-事件日志(Event Logs):记录合约执行过程中触发的事件,便于调试和追踪 + +-ABI接口调用:允许用户通过浏览器直接与合约交互,执行查询或触发函数 + + +2.5 网络状态监控 + +-节点地理分布:地图可视化展示全球节点的地理位置 + +-协议版本检测:监控网络中不同客户端(如Geth、Parity)的版本兼容性 + +-分叉警报:实时检测硬分叉或软分叉事件,并提供链重组(Reorg)详情 + + + +3.核心特点 + +区块链浏览器的设计理念和技术实现赋予其以下显著特点: + +3.1 数据透明性与不可篡改性 + +-所有链上数据公开可查,任何人可通过地址、交易哈希等查询交易详情(如金额、时间戳、参与方地址,任何用户均可验证交易的真实性 + +-基于哈希指针和加密算法,确保历史数据无法被修改 + +-提供区块结构可视化展示,包括区块高度、哈希值、矿工奖励、打包交易数量等元数据 + +-实时显示网络全局数据:全网算力、交易手续费均值、未确认交易池规模、活跃地址数量等 + + +3.2 多链兼容性 + +-支持不同共识机制(如PoW、PoS、DPoS)的区块链,例如比特币、以太坊、波场等 + +-跨链浏览器(如Blockchair)可同时查询多条链的数据 + +-各公链浏览器存在协议级差异(如UTXO模型与账户模型的不同展示逻辑) + +-智能合约链支持ABI解析,可将字节码转化为可读函数调用 + +-特定功能定制化(如Filecoin浏览器包含存储订单生命周期追踪) + + +3.3 安全防护机制 + +-采用速率限制策略(如单个IP每分钟不超过120次请求) + +-部署交易流量异常检测系统,自动识别DDoS攻击模式 + +-关键查询实施人机验证(CAPTCHA),防止数据爬取滥用 + + +3.4 用户友好性 + +-提供地址标签、交易备注等个性化功能,降低普通用户的理解门槛 + +-支持多语言界面及移动端适配,提升访问便捷性 + + +3.5 功能完备性 + +-开放API接口供第三方应用调用,例如获取地址余额或生成交易二维码 + +-内置调试工具(如以太坊的Remix插件),简化智能合约开发流程 + +-支持12种以上查询维度:地址资产图谱、双花交易检测、富豪地址追踪、Gas消耗分析等 + +-集成链上分析工具:代币流向图谱、MEV交易监控、NFT元数据解析等进阶功能 + + +4.应用场景 + +-普通用户:确认转账到账、验证钱包地址有效性 + +-开发者:调试智能合约、监控合约事件日志 + +-研究者:分析链上活动(如NFT交易趋势、DeFi协议资金流动) + +-监管机构:追踪非法交易(如洗钱)或调查黑客事件 + +-交易验证:用户支付加密货币后,可通过浏览器确认交易是否成功上链 + +-链上审计:监管机构利用浏览器追踪资金流向,打击洗钱行为 + +-DApp开发:开发者分析热门合约的调用模式,优化产品设计 + +-市场分析:投资者通过统计图表评估网络活跃度,辅助投资决策 + + + +总结 + +区块链浏览器作为连接用户与区块链网络的桥梁,通过高效的数据解析和可视化技术,实现了链上信息的透明化访问。其技术架构融合了分布式系统、数据库优化和前端交互设计,核心功能覆盖了从基础查询到智能合约调试的全场景需求。随着区块链技术的普及,浏览器将进一步向跨链聚合、隐私保护(如零知识证明集成)和AI数据分析等方向演进,成为Web3生态不可或缺的基础设施。 + + + +下面以Mempool为例介绍常见区块链浏览器的具体内容和使用方式: + +Mempool.space 是一个开源的比特币区块链浏览器,提供了实时的区块链数据和交易信息,其核心功能与特性如下: + +1.核心定位 + +专注比特币内存池可视化,展示全网待确认交易池状态 + +提供链下交易动态追踪(区别于传统区块链浏览器侧重链上数据) + + +2. 核心功能模块 + +2.1交易池监控 + +-实时显示全网未确认交易数量(典型范围:3万-30万笔) + +-按交易费率(sat/vB)分层统计交易分布(如0-1 sat、1-5 sat等区间) + +-可视化交易池容量变化曲线(支持1小时/24小时/7天时间维度) + + +2.2费率预测 + +-智能计算不同确认优先级所需费率(10分钟/3小时/24小时确认对应的建议费率) + +-动态调整预测模型,考虑区块空间竞争强度与矿工打包策略 + + +2.3交易溯源 + +输入交易ID可查看其在内存池中的存活时间(最长滞留交易警示) + +展示交易在费率分层中的相对位置(前10%/50%等) + + +2.4监控网络状态 + +实时哈希率监测(单位:EH/s) + +区块间隔时间统计(最近10个区块平均出块时间) + +内存池交易总容量(按vMB和交易数量双维度展示) + + + +3. 技术实现特性 + +-部署全球监测节点网络(超30个节点覆盖主要矿池区域) + +-采用流数据处理架构,延迟控制在2秒内(对比常规浏览器5-10秒延迟) + +-构建交易传播路径图谱(可视化交易在节点间的广播过程) + + + +4. 特色数据分析 + +4.1矿工行为分析 + +-识别矿池交易打包偏好(如特定矿池的费率优先级) + +-统计空区块产生频率与关联矿池 + +4.2交易替换监控 + +-检测RBF(费用替代)交易对原始交易的替换情况 + +-标记CPFP(子为父付费)交易捆绑策略 + + + +5. 用户场景 + +-普通用户:优化交易费设置(避免超额支付或长时间滞留) + +交易所:监控提现交易状态(预估到账时间) + +开发者:调试交易广播策略(测试网络拥堵时的传播效率) + +研究人员:分析比特币网络健康度(内存池积压周期规律) + +该平台日均访问量超50万次(2024年数据),其提供的交易加速服务通过合作矿池实现优先打包,成功率可达92%。与同类工具(如mempool.observer)相比,其优势在于深度整合闪电网络数据,提供通道容量平衡状态的可视化分析。 + + + +以下是如何使用 Mempool.space 的详细教程: + +1.访问 Mempool.space + +打开浏览器,访问 [Mempool.space](https://mempool.space/) + + +2.主界面概览 + +打开 mempool.space 后,主界面分为几个核心区域: + +-顶部导航栏:包含搜索框、网络切换(比特币主网/测试网)、语言设置等 + +-实时手续费估算:显示当前不同优先级(高/中/低)交易的推荐手续费率(单位:sat/vB) + +-内存池交易列表:动态展示当前未确认交易的列表。每笔交易显示: + -手续费率(sat/vB) + -交易大小(KB) + -总手续费(BTC 或 satoshi) + -交易时间(多久前进入内存池) + +-最新区块:显示最新挖出的区块高度、包含的交易数量、矿工手续费等 + +-矿工信息:展示近期区块的矿工(矿池)分布统计 + + +3.查看区块信息 + +点击首页的区块高度链接,进入区块详情页面: + +-区块哈希:显示区块的哈希值 + +-时间戳:显示区块被挖出的时间 + +-交易数量:显示区块中包含的交易数量 + +-矿工奖励:显示矿工获得的奖励 + +-区块大小:显示区块的大小,点击任意区块高度,可查看该区块的详细信息: + -区块哈希、时间戳、矿工(矿池) + -区块内包含的交易数量及总手续费 + -区块大小、权重、Coinbase 交易(矿工奖励) + +-区块版本:显示区块的版本号 + + +4.查看交易信息 + +在首页或区块详情页面,点击任意交易哈希,进入交易详情页面: + +-交易哈希:显示交易的哈希值 + +-输入和输出:显示交易的输入和输出地址及金额 + +-交易费:显示交易的手续费 + +-确认状态:显示交易的确认状态和确认次数 + +-交易大小:显示交易的大小 + +-锁定时间:显示交易的锁定时间(如果有) + +-查看推荐手续费:在主界面中间的图表中,可以看到不同优先级(高/中/低)的手续费率(sat/vB)。图表横轴是时间,纵轴是手续费率,显示过去一段时间内的费率波动 + -快速确认(绿色):支付更高费率,交易可能在下一个区块被确认 + -中等确认(黄色):平衡费率,可能在1-6个区块内确认 + -经济模式(红色):最低费率,但可能需要等待更长时间 + +-调整手续费模式:点击图表右上角的「经济模式」或「加速模式」,可切换手续费计算逻辑(保守型或激进型) + + +5.使用搜索功能 +在页面顶部的搜索栏中输入区块哈希、交易哈希或比特币地址,按下回车键进行搜索: + +-搜索交易ID:在顶部搜索栏输入完整的交易哈希(Transaction Hash),即可查看该交易的详细信息,包括: + -确认状态(未确认/已确认的区块高度) + -输入/输出地址及金额 + -手续费(总手续费、手续费率 sat/vB) + -交易体积(vBytes)和权重(Weight) + +-区块哈希:显示对应区块的详细信息 + +-交易哈希:显示对应交易的详细信息 + +-比特币地址:输入比特币地址,可查看该地址的余额、交易历史记录(转入/转出)、UTXO(未花费的交易输出)等 + + +6.高级功能 + +-替换手续费(RBF)和子父手续费(CPFP): + -如果一笔未确认交易支持 RBF(Replace-By-Fee),可在交易详情页点击「IncreaseFee」重新广播更高手续费的交易。 + -如果交易有找零输出,可通过 CPFP(Child-Pays-For-Parent)创建子交易支付更高手续费,加速父交易确认。 + +-自建节点监控:Mempool.space 支持连接到自己的比特币全节点(需在设置中配置),实现私有化监控 + +-实时图表:在首页底部可以看到实时更新的图表,包括交易费率、内存池大小等 + +-API 接口:Mempool.space 提供了 API 接口,开发者可以使用这些接口获取区块链数据。可以在 [API 文档](https://mempool.space/docs/api) 中找到详细信息 + +-自定义设置:在页面右上角的设置图标中,可以调整显示语言、主题(浅色或深色)等个性化设置。 + + + +Reference: -[how-to-use-mempool-space-block-explorer](https://bitcoinbriefly.com/how-to-use-mempool-space-block-explorer/) diff --git a/BTC/Basic/hdwallet/README.md b/BTC/Basic/hdwallet/README.md new file mode 100644 index 000000000..b1562429c --- /dev/null +++ b/BTC/Basic/hdwallet/README.md @@ -0,0 +1,37 @@ +HD钱包,全称为分层确定性钱包(Hierarchical Deterministic Wallet),是一种通过单一的种子(Seed)生成和管理多个密钥对的数字钱包。这种结构使得用户只需备份一次种子,即可控制所有衍生出的私钥和公钥,极大地简化了密钥管理的复杂性。 + +**HD钱包的主要特点:** + +1. **分层结构:** HD钱包采用树状结构,根节点由主密钥(Master Key)组成,子节点为从主密钥派生出的子密钥。这种分层设计允许用户根据需要生成不同层级的密钥,方便管理和分配。 + +2. **确定性:** 通过相同的种子,HD钱包可以生成相同的密钥序列。这意味着只要备份了种子,就可以在任何时候恢复所有的密钥和地址。 + +3. **增强的隐私性:** HD钱包可以为每笔交易生成不同的地址,避免重复使用同一地址,从而提高交易的隐私性。 + +**HD钱包的工作原理:** + +- **种子生成:** 用户首先生成一个随机数作为种子,通常通过助记词(Mnemonic)表示。助记词是一组易于记忆的单词序列,用于表示种子。 + +- **主密钥派生:** 通过种子,使用特定的算法(如HMAC-SHA512)生成主私钥和主公钥。 + +- **子密钥派生:** 从主密钥可以派生出多个子密钥,每个子密钥又可以派生出自己的子密钥,形成树状结构。这种派生过程可以根据需要进行多层次的扩展。 + +**HD钱包的优势:** + +- **简化备份:** 用户只需备份一次种子或助记词,即可恢复所有的密钥和地址,避免了逐个备份每个私钥的麻烦。 + +- **灵活的密钥管理:** 分层结构允许用户根据不同的用途或账户生成不同的子密钥,方便管理。 + +- **增强的安全性:** 通过生成不同的地址,减少了地址关联性,提升了交易的隐私性和安全性。 + +**HD钱包的标准:** + +HD钱包的概念最早由BIP32提案提出,随后BIP39引入了助记词的标准,BIP44则定义了多币种、多账户的路径标准。这些标准共同构建了当前HD钱包的基础。 + +**注意事项:** + +- **妥善保管助记词:** 助记词是恢复钱包的唯一凭证,一旦丢失或泄露,可能导致资产无法找回或被盗。 + +- **定期备份:** 虽然HD钱包简化了备份过程,但仍需定期检查备份的有效性,确保在需要时能够成功恢复。 + +总的来说,HD钱包通过分层确定性的设计,提供了高效、安全且灵活的密钥管理方式,已成为当前数字货币钱包的主流选择。 \ No newline at end of file diff --git a/BTC/Basic/hdwallet/derive.md b/BTC/Basic/hdwallet/derive.md new file mode 100644 index 000000000..442c1585c --- /dev/null +++ b/BTC/Basic/hdwallet/derive.md @@ -0,0 +1,124 @@ +HD钱包(Hierarchical Deterministic Wallet)通过分层确定性结构生成和管理密钥对。其核心在于通过种子生成主密钥,然后通过分层推导生成多个子密钥。这种方式使得密钥管理变得简单、安全,用户只需备份种子即可恢复整个钱包。以下是HD钱包的详细技术细节和子密钥推导过程。 + +### 1. **HD钱包结构概述** +- **种子(Seed)**:HD钱包从一个随机生成的种子开始,该种子通过助记词(BIP39)表示。 +- **主密钥(Master Key)**:由种子生成,包含主私钥、主公钥和链码。 +- **链码(Chain Code)**:用于生成子密钥,是密钥派生过程中的关键成分。 +- **子密钥**:从主密钥派生而来,每个子密钥都有自己的私钥、公钥和链码。 + +### 2. **种子生成** +- **助记词**:助记词由熵(随机位数)通过哈希和校验生成。例如,一个128位的熵可以生成12个助记词。 +- **种子生成算法**: + - 助记词经过PBKDF2(Password-Based Key Derivation Function 2)算法与一个盐值(通常是 `mnemonic` + 密码短语)组合,生成512位种子。 + +### 3. **主密钥生成** +- 使用HMAC-SHA512算法,以种子作为输入,和常量字符串“Bitcoin seed”作为HMAC密钥来生成512位输出。 +- 输出前256位为主私钥(`m`),后256位为链码(`c`)。 + + **HMAC计算公式**: + ``` + I = HMAC-SHA512(key="Bitcoin seed", data=seed) + ``` + ``` + master_private_key = I[0:32] + master_chain_code = I[32:64] + ``` + +### 4. **子密钥推导过程** +子密钥推导是HD钱包中生成子密钥的核心过程,可以通过**标准派生**和**硬化派生**来实现。 + +#### a. **标准派生(Non-hardened derivation)** +- 使用父公钥和链码推导子公钥,公钥和链码可以生成下一级的公钥链,但无法推导出私钥。 +- 算法: + ``` + I = HMAC-SHA512(key=cpar, data=serP(Kpar) || ser32(i)) + ``` + - `cpar`:父链码。 + - `serP(Kpar)`:父公钥的序列化形式。 + - `ser32(i)`:索引值的32位表示。 + - `I` 分成两部分:`IL`(左半部分)和 `IR`(右半部分)。 + +- 子私钥计算公式: + ``` + ki = (IL + kpar) % n + ci = IR + ``` + - `ki`:子私钥。 + - `n`:椭圆曲线的阶。 + +#### b. **硬化派生(Hardened derivation)** +- 使用父私钥生成子私钥。硬化派生增加了安全性,不能从子公钥推导出父密钥。 +- 算法: + ``` + I = HMAC-SHA512(key=cpar, data=0x00 || ser256(kpar) || ser32(i)) + ``` + - `0x00` 前缀用于表示该派生使用的是私钥。 + - `kpar` 是父私钥。 + - `ci = IR` 是子链码。 + +- 子私钥计算公式: + ``` + ki = (IL + kpar) % n + ci = IR + ``` + +### 5. **路径表示** +BIP44引入了用于多币种和多账户的标准路径表示: +``` +m / purpose' / coin_type' / account' / change / address_index +``` +- **`purpose'`**:通常为44',表示使用BIP44规范。 +- **`coin_type'`**:用于区分不同的加密货币,比如0'代表比特币,60'代表以太坊。 +- **`account'`**:账户编号,用于分离不同的账户。 +- **`change`**:0表示外部地址,1表示找零地址。 +- **`address_index`**:地址在账户中的索引。 + +同时在BIP49和BIP84中也基于BIP44做了扩展,形成我们现在经常见到的BTC多个地址 + +#### Legacy地址 - BIP44 +![Legacy:BIP44](./imgs/hd-wallets-derivation-paths-bip44.png) + +#### P2SH地址 - BIP49 +![Legacy:BIP49](./imgs/hd-wallets-derivation-paths-bip49.png) + +#### P2WPKH地址 - BIP84 +![Legacy:BIP84](./imgs/hd-wallets-derivation-paths-bip84.png) + + +### 6. **ECC与HD钱包** +HD钱包使用椭圆曲线加密(Elliptic Curve Cryptography, ECC)来确保公钥和私钥的安全性。公钥由私钥通过椭圆曲线点乘生成。标准加密算法如 `secp256k1` 在HD钱包中被广泛使用。 + +**公钥生成公式**: +``` +P = k * G +``` +- `P` 是公钥,`k` 是私钥,`G` 是椭圆曲线的生成点。 + +### 7. **实现细节示例** +使用JavaScript库实现HD钱包的生成和子密钥推导: + +```javascript +const bip32 = require('bip32'); +const bip39 = require('bip39'); + +// 生成助记词和种子 +const mnemonic = bip39.generateMnemonic(); +const seed = bip39.mnemonicToSeedSync(mnemonic); + +// 从种子生成HD钱包根节点 +const root = bip32.fromSeed(seed); + +// 派生路径:m/44'/0'/0'/0/0 +const child = root.derivePath("m/44'/0'/0'/0/0"); + +console.log('助记词:', mnemonic); +console.log('子公钥:', child.publicKey.toString('hex')); +console.log('子私钥:', child.toWIF()); +``` + +### 8. **安全性与注意事项** +- **助记词备份**:确保助记词安全存储,一旦丢失或泄露,可能导致资产永久损失或被盗。 +- **硬化派生的使用**:硬化派生确保公钥无法推导出父级私钥,提高了安全性,建议在生成重要的子密钥时使用。 + +### **总结** +HD钱包通过分层结构和确定性算法,为用户提供了简单的密钥管理方式。其分层树结构和子密钥推导机制使得一个种子就能生成无限数量的私钥和公钥,极大地提高了用户体验和安全性。 \ No newline at end of file diff --git a/BTC/Basic/hdwallet/extendKeys.md b/BTC/Basic/hdwallet/extendKeys.md new file mode 100644 index 000000000..9102d4de8 --- /dev/null +++ b/BTC/Basic/hdwallet/extendKeys.md @@ -0,0 +1,104 @@ +HD钱包中的**扩展公钥(Extended Public Key, xPub)**和**扩展私钥(Extended Private Key, xPriv)**是BIP32标准的一部分,它们是用于派生子密钥和管理钱包的关键概念。以下是它们的详细技术介绍。 + +### 1. **扩展密钥的定义** +- **扩展私钥(xPriv)**:包含私钥和链码的信息,允许派生出子私钥和子公钥。 +- **扩展公钥(xPub)**:包含公钥和链码的信息,只能派生出子公钥,无法推导出私钥。 + +![extend keys](./imgs/hd-wallets-extended-keys.png) + +### 2. **扩展密钥的组成** +扩展密钥不仅包括公钥或私钥,还包括链码(Chain Code)、路径信息等,以支持分层确定性派生。扩展密钥的完整组成如下: + +- **公钥/私钥**:用于加密和签名的密钥。 +- **链码**:确保子密钥派生时的不可预测性,提供额外的随机性。 +- **深度(Depth)**:当前密钥的层级深度,根节点为0。 +- **父公钥指纹(Parent Fingerprint)**:标识父密钥,用于防止路径冲突。 +- **索引(Index)**:指示该密钥在父密钥下的派生索引。 +- **版本(Version)**:指示密钥类型(xPub 或 xPriv)。 +- **校验和**:用于验证密钥完整性。 + +### 3. **扩展密钥的编码格式** +扩展密钥通常以Base58Check格式进行编码,确保其易于人类读取和传输。Base58Check编码避免了易混淆的字符(如0和O,l和I)。编码后的扩展密钥通常以以下前缀开头: +- **xPub**:用于表示扩展公钥。 +- **xPriv**:用于表示扩展私钥。 + +#### **编码示例** +扩展密钥的编码格式如下: +``` +[version (4 bytes)][depth (1 byte)][parent fingerprint (4 bytes)][child number (4 bytes)] +[chain code (32 bytes)][key data (33 bytes)][checksum (4 bytes)] +``` + +### 4. **生成扩展私钥和扩展公钥** +#### a. **生成扩展私钥** +扩展私钥是通过HMAC-SHA512生成的。算法步骤如下: +- 生成主私钥和链码时,将种子输入HMAC-SHA512,并使用“Bitcoin seed”作为密钥。 +- 输出前32字节是私钥,后32字节是链码。 + +#### b. **生成扩展公钥** +从扩展私钥可以生成扩展公钥,通过椭圆曲线加密(ECC)运算将私钥转换为公钥。 + +**扩展公钥生成步骤**: +- 取扩展私钥的前32字节(私钥部分),并将其转换为公钥。 +- 保留链码和其他元数据。 + +### 5. **子密钥派生过程** +扩展密钥的强大之处在于,它们支持通过链码进行分层确定性派生,生成子密钥而不需要暴露父级的私钥。 + +#### a. **从扩展私钥派生子私钥** +扩展私钥可以派生子私钥和子公钥。 +- **算法**: + ``` + I = HMAC-SHA512(key=parent_chain_code, data=0x00 || parent_private_key || child_index) + ``` + - `I` 的前32字节用作派生的子私钥的一部分,后32字节用作新的子链码。 + - 子私钥公式:`child_private_key = (IL + parent_private_key) % n`。 + +#### b. **从扩展公钥派生子公钥** +扩展公钥只能派生子公钥,无法反推出私钥,确保了安全性。 +- **算法**: + ``` + I = HMAC-SHA512(key=parent_chain_code, data=parent_public_key || child_index) + ``` + - 子公钥通过公钥加法计算得出。 + - 子公钥公式:`child_public_key = G * IL + parent_public_key`(`G` 为椭圆曲线生成点)。 + + +![why-it-works](./imgs/hd-wallets-extended-keys-why-it-works.png) + +### 6. **硬化派生与标准派生** +- **硬化派生**(Hardened Derivation):只能通过扩展私钥派生,子公钥无法通过扩展公钥派生出来,增加了安全性。索引大于等于 `2^31` 的值用于硬化派生。 +- **标准派生**(Non-Hardened Derivation):可以通过扩展公钥派生出子公钥,索引小于 `2^31`。 + +### 7. **用途和安全性** +- **xPub的应用**:适用于公开应用,如钱包软件中用来显示和管理多个收款地址。通过扩展公钥,第三方可以生成新的地址而不接触私钥。 +- **xPriv的应用**:仅用于安全环境,绝不能公开,以防止密钥泄露。 + +**安全注意事项**: +- **保护扩展私钥**:xPriv的泄露意味着整个密钥树的私钥都处于危险中。 +- **使用硬化派生**:在可能被公开的路径中,建议使用硬化派生来确保私钥安全。 + +### 8. **示例代码** +以下是使用JavaScript库生成扩展密钥的示例代码: + +```javascript +const bip32 = require('bip32'); +const bip39 = require('bip39'); + +// 生成助记词和种子 +const mnemonic = bip39.generateMnemonic(); +const seed = bip39.mnemonicToSeedSync(mnemonic); + +// 生成根节点 +const root = bip32.fromSeed(seed); + +// 获取扩展公钥和扩展私钥 +const xPriv = root.toBase58(); +const xPub = root.neutered().toBase58(); + +console.log('扩展私钥 (xPriv):', xPriv); +console.log('扩展公钥 (xPub):', xPub); +``` + +### **总结** +扩展公钥(xPub)和扩展私钥(xPriv)是HD钱包中的核心概念,提供了派生和管理子密钥的能力。xPriv 可以派生子私钥和子公钥,而 xPub 只能派生子公钥。使用这些扩展密钥,用户和开发者可以实现灵活的密钥管理,同时保证密钥的安全性。 \ No newline at end of file diff --git a/BTC/Basic/hdwallet/imgs/entry-to-mnemonic.png b/BTC/Basic/hdwallet/imgs/entry-to-mnemonic.png new file mode 100644 index 000000000..6c910099d Binary files /dev/null and b/BTC/Basic/hdwallet/imgs/entry-to-mnemonic.png differ diff --git a/BTC/Basic/hdwallet/imgs/hd-wallets-derivation-paths-bip44.png b/BTC/Basic/hdwallet/imgs/hd-wallets-derivation-paths-bip44.png new file mode 100644 index 000000000..53548c84e Binary files /dev/null and b/BTC/Basic/hdwallet/imgs/hd-wallets-derivation-paths-bip44.png differ diff --git a/BTC/Basic/hdwallet/imgs/hd-wallets-derivation-paths-bip49.png b/BTC/Basic/hdwallet/imgs/hd-wallets-derivation-paths-bip49.png new file mode 100644 index 000000000..bd1db95d5 Binary files /dev/null and b/BTC/Basic/hdwallet/imgs/hd-wallets-derivation-paths-bip49.png differ diff --git a/BTC/Basic/hdwallet/imgs/hd-wallets-derivation-paths-bip84.png b/BTC/Basic/hdwallet/imgs/hd-wallets-derivation-paths-bip84.png new file mode 100644 index 000000000..fc6667196 Binary files /dev/null and b/BTC/Basic/hdwallet/imgs/hd-wallets-derivation-paths-bip84.png differ diff --git a/BTC/Basic/hdwallet/imgs/hd-wallets-extended-keys-why-it-works.png b/BTC/Basic/hdwallet/imgs/hd-wallets-extended-keys-why-it-works.png new file mode 100644 index 000000000..79cb14a13 Binary files /dev/null and b/BTC/Basic/hdwallet/imgs/hd-wallets-extended-keys-why-it-works.png differ diff --git a/BTC/Basic/hdwallet/imgs/hd-wallets-extended-keys.png b/BTC/Basic/hdwallet/imgs/hd-wallets-extended-keys.png new file mode 100644 index 000000000..4615602eb Binary files /dev/null and b/BTC/Basic/hdwallet/imgs/hd-wallets-extended-keys.png differ diff --git a/BTC/Basic/hdwallet/imgs/mnemonic-to-seed.jpeg b/BTC/Basic/hdwallet/imgs/mnemonic-to-seed.jpeg new file mode 100644 index 000000000..664314fae Binary files /dev/null and b/BTC/Basic/hdwallet/imgs/mnemonic-to-seed.jpeg differ diff --git a/BTC/Basic/hdwallet/imgs/seed-to-root.png b/BTC/Basic/hdwallet/imgs/seed-to-root.png new file mode 100644 index 000000000..dfdbb47d4 Binary files /dev/null and b/BTC/Basic/hdwallet/imgs/seed-to-root.png differ diff --git a/BTC/Basic/hdwallet/mnemonic.md b/BTC/Basic/hdwallet/mnemonic.md new file mode 100644 index 000000000..9d358a867 --- /dev/null +++ b/BTC/Basic/hdwallet/mnemonic.md @@ -0,0 +1,88 @@ +HD钱包中的助记词(Mnemonic Phrase)是一组人类可读的单词,用来表示随机生成的种子。助记词为用户提供了一个易于备份和恢复的钱包方式,极大地简化了复杂的密钥管理。以下是HD钱包助记词的详细技术原理: + +### 1. **助记词的定义和作用** +助记词是一串用来帮助用户记忆种子的单词序列。HD钱包使用助记词来生成用于派生密钥的种子,因此只要备份助记词,就可以恢复整个钱包的密钥树。 + +### 2. **助记词生成的步骤** +助记词生成过程基于BIP39标准,涉及以下步骤: + +#### a. **生成熵(Entropy)** +- **熵的定义**:熵是用于随机生成助记词的随机位序列。常见的熵长度有128位、160位、192位、224位和256位。 +- **例子**:假设熵为128位,它会生成12个单词的助记词。 + +#### b. **计算校验和(Checksum)** +- 将熵进行SHA-256哈希,取哈希结果的前 `ENT / 32` 位作为校验和,其中 `ENT` 是熵的位长度。 +- **例子**:如果熵是128位,则校验和为 `128 / 32 = 4` 位。 + +#### c. **组合熵和校验和** +- 将原始熵和校验和组合,形成一个新的位序列。例如,128位熵加上4位校验和形成132位序列。 + +#### d. **分割为11位块** +- 将132位的位序列分割为多个11位的块,每个块可以表示0到2047之间的一个数字。 +- 这些数字用于从一个2048个单词的字典(BIP39单词表)中查找对应的单词。 + + +![entry-to-mnemonic.png](./imgs/entry-to-mnemonic.png) + +### 3. **BIP39单词表** +- **固定单词表**:BIP39定义了一个包含2048个单词的标准单词表。这些单词具有以下特点: + - 每个单词都有明确的拼写,不易混淆。 + - 单词表的不同语言版本保持一致性,但使用的是本地语言词汇。 + +### 4. **种子生成** +助记词本身并不能直接用于密钥派生。助记词需要经过密码学处理生成一个种子,种子用来生成HD钱包的主私钥和链码。 + +#### a. **PBKDF2函数** +- 助记词通过PBKDF2-HMAC-SHA512算法与一个盐值组合,生成512位种子。 +- **盐值**:通常是 `mnemonic` 加上一个用户提供的密码短语(可选),增强了安全性。 +- **算法**: + ```text + seed = PBKDF2(mnemonic, "mnemonic" + passphrase, 2048, 64, HMAC-SHA512) + ``` + + +![mnemonic-to-seed.png](./imgs/mnemonic-to-seed.jpeg) + + +### 5. **助记词的恢复** +当用户输入助记词进行恢复时,钱包软件会执行以下步骤: +- 将助记词映射回原始熵和校验和。 +- 验证助记词是否有效(检查校验和)。 +- 使用PBKDF2函数将助记词和用户输入的密码短语(如果有)转换为种子。 +- 通过该种子生成HD钱包的主私钥和链码,从而恢复整个密钥树。 + + +![seed-to-root.png](./imgs/seed-to-root.png) + + +### 6. **助记词的优点** +- **易于记忆和备份**:助记词比纯随机的密钥更容易记住和存储。 +- **便于恢复**:只要用户保留助记词,就可以在任何支持BIP39的HD钱包中恢复整个钱包。 +- **增加安全性**:通过密码短语增强了助记词的安全性。如果助记词被泄露,攻击者仍然需要知道密码短语才能恢复钱包。 + +### 7. **助记词的安全注意事项** +- **备份和存储**:助记词应以离线方式备份,如写在纸上并存放在安全的地方。不要将助记词存储在联网设备上。 +- **密码短语**:使用一个复杂且不易猜测的密码短语可以大大提高安全性,即使助记词被盗,攻击者也无法轻易访问钱包。 +- **防止物理和电子威胁**:确保助记词的存储位置防范火灾、盗窃或电子攻击。 + +### 8. **技术示例** +以下是使用JavaScript库生成助记词和种子的示例代码: + +```javascript +const bip39 = require('bip39'); + +// 生成12个单词的助记词 +const mnemonic = bip39.generateMnemonic(); +console.log('助记词:', mnemonic); + +// 验证助记词是否合法 +const isValid = bip39.validateMnemonic(mnemonic); +console.log('助记词是否合法:', isValid); + +// 将助记词转换为种子 +const seed = bip39.mnemonicToSeedSync(mnemonic, '可选的密码短语'); +console.log('生成的种子:', seed.toString('hex')); +``` + +### **总结** +HD钱包的助记词系统提供了一种安全、便捷的方式来管理和恢复钱包。助记词通过熵、校验和、PBKDF2算法生成种子,从而衍生出一棵密钥树。理解助记词的原理有助于用户更好地保护自己的数字资产,同时也能帮助开发者实现符合BIP39标准的钱包应用。 \ No newline at end of file diff --git a/BTC/Basic/payment/P2PKH.md b/BTC/Basic/payment/P2PKH.md new file mode 100644 index 000000000..8a086dba0 --- /dev/null +++ b/BTC/Basic/payment/P2PKH.md @@ -0,0 +1,87 @@ +# 简介 + +[P2PKH(Pay-to-Public-Key-Hash)](../wallet/address.js#L42)是比特币网络中最常用的一种交易类型,设计用来将比特币支付到某个具体的公钥哈希,而不是直接到公钥本身。这种方式提供了更高的安全性和隐私保护。在P2PKH交易中,比特币被发送到一个地址,这个地址实际上是持有者公钥的哈希版本。这种地址通常以数字"1"开头。地址不仅简洁,而且通过哈希公钥,隐藏了公钥的实际内容,增加了隐私保护层。 + +# 工作过程 + +## ScriptPubKey(锁定脚本) + +P2PKH脚本模式包含一个公钥哈希,该哈希和以下操作码共同构成锁定脚本 + +```js +// 模版 +OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG + +OP_DUP // 复制栈顶的元素 +OP_HASH160 // 弹出栈顶元素,计算其SHA-256散列,然后计算RIPEMD-160散列 +OP_PUSHBYTES_20 // 推送20字节的公钥哈希到栈顶 +18e1fad25b2983d5dbb2e2b96e3ce756a69b3bc2 // 公钥哈希 +OP_EQUALVERIFY // 比较栈顶元素是否匹配 +OP_CHECKSIG // 验证数字签名 + +``` + +## ScriptSig(解锁脚本) + +要解锁花费此脚本,上面公钥的所有者需要提供原始公钥以及的有效签名 + +```js +// 模版 + + +OP_PUSHBYTES_72 // 推送72字节到栈顶 +3045022100c233c3a8a510e03ad18b0a24694ef00c78101bfd5ac075b8c1037952ce26e91e02205aa5f8f88f29bb4ad5808ebc12abfd26bd791256f367b04c6d955f01f28a772401 // 签名数据 +OP_PUSHBYTES_33 // 推送33字节到栈顶 +03480b6822120e9936b43859d84c380583c3d0292409b21453ae962815090f8117 // 压缩公钥 +``` + +## 执行过程 + +1. 脚本合并,ScriptSig在前ScriptPubKey在后 + +```js + OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG +``` + +2. 栈内执行 + +- OP_DUP 从栈里取出公钥,复制并做HASH160推进栈顶 +- OP_EQUALVERIFY 对比上一步的计算结果和Public Key Hash +- OP_CHECKSIG 校验数字签名,如果通过锁定脚本合法可以花费对应UTXO,如果不通过交易失败 + +## 地址生成 + +P2PKH支付方式从公钥生成地址遵循以下详细步骤: + +### 步骤1: 计算公钥哈希 + +1. **计算公钥的SHA-256哈希值**:首先,对公钥进行SHA-256哈希运算。 +2. **计算RIPEMD-160哈希值**:然后,对SHA-256的结果再进行RIPEMD-160哈希运算。这两步哈希运算的结果称为公钥哈希(PKH)。 + +### 步骤2: 添加版本字节 + +- **添加版本字节**:在公钥哈希前添加一个版本字节(比特币主网络的P2PKH地址的版本是0x00, 测试网是0x6f)。这有助于钱包软件识别和处理不同类型的地址。 + +### 步骤3: 计算校验和 + +1. **双重SHA-256哈希**:对带有版本字节的公钥哈希进行两次SHA-256哈希运算。 +2. **取前四个字节**:从双重哈希的结果中取出前四个字节,这部分称为校验和。 + +### 步骤4: 生成地址 + +- **组合和Base58编码**:将版本字节和公钥哈希以及校验和组合在一起,整个结构为: + ```js + [version byte][public key hash][checksum] + ``` + 然后,对整个字节串进行Base58编码,以生成最终的P2PKH地址。 + +### 示例 + +假设有一个公钥,步骤如下: + +1. **公钥**: `022c3ee7cdb92394e32e82ec5bc8860c8888df6a9910537e90c75079726a2a8469` +2. **SHA-256哈希**:计算结果: `98a21cf747c0fcd80845afffa2260feb64aa7cc2b73ac681407b25229bad3bbc`。 +3. **RIPEMD-160哈希**:计算结果: `999ff7726530ed0d0a7eb3b7442c5143f643f638`。 +4. **添加版本字节**:`00` + [RIPEMD-160哈希结果]。 +5. **计算校验和**:对上述结果执行两次SHA-256,取前四个字节。 +6. **Base58编码**:将最终字节串转换为Base58编码:`1F1J22NAAgnNHXNojT7W8GfP9JSXXjTV8N` diff --git a/BTC/Basic/payment/P2SH-P2PKH.md b/BTC/Basic/payment/P2SH-P2PKH.md new file mode 100644 index 000000000..3ec7e608d --- /dev/null +++ b/BTC/Basic/payment/P2SH-P2PKH.md @@ -0,0 +1,113 @@ +# 简介 + +[P2SH-P2PKH(Pay to Script Hash - Pay to Public Key Hash)](../wallet/address.js#L43)是一种混合地址类型,结合了P2SH(支付到脚本哈希)和P2PKH(支付到公钥哈希)的特点。这种地址类型旨在增强比特币交易的灵活性和安全性。`P2SH`是在2012年通过比特币改进提案BIP 0016引入的,允许用户发送比特币到一个脚本的哈希值,而不是直接发送到公钥的哈希值。这样做的好处是可以隐藏脚本的具体细节,只有在花费比特币时才需要揭示。这对于实现更复杂的交易类型(如多重签名)非常有用。`P2SH-P2PKH`结合了`P2SH`和`P2PKH`的特性。在这种地址类型中,交易输出不是直接支付到公钥哈希,而是支付到一个包含`P2PKH`脚本的`P2SH`地址。`P2SH-P2PKH`地址通常以“3”开始,不同于传统的`P2PKH`地址,后者以“1”开始。这种地址类型是比特币地址方案中的一种进阶形式,适用于需要额外安全或复杂交易类型的用户。 + +# 工作过程 + +## ScriptPubKey(锁定脚本) + +在P2SH-P2PKH交易中,锁定脚本不直接包含传统的P2PKH脚本。相反,它包含了对应P2PKH脚本的哈希值。锁定脚本的形式如下: + +```js +// 模版 +OP_HASH160 OP_EQUAL +其中 +scriptHash = hash160(OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG) + +OP_HASH160 // 弹出栈顶元素,计算其SHA-256散列,然后计算RIPEMD-160散列 +OP_PUSHBYTES_20 // 推送20字节到栈顶 +18e1fad25b2983d5dbb2e2b96e3ce756a69b3bc2 // 脚本哈希 +OP_EQUAL // 比较栈顶两个元素相等 +``` + +## ScriptSig(解锁脚本) + +要解锁花费此脚本,上面公钥的所有者需要提供公钥,有效签名以及原始脚本 + +```js +// 模版 + + +OP_PUSHBYTES_72 // 推送72字节到栈顶 +3045022100c233c3a8a510e03ad18b0a24694ef00c78101bfd5ac075b8c1037952ce26e91e02205aa5f8f88f29bb4ad5808ebc12abfd26bd791256f367b04c6d955f01f28a772401 // 签名数据 +OP_PUSHBYTES_33 // 推送33字节到栈顶 +03480b6822120e9936b43859d84c380583c3d0292409b21453ae962815090f8117 // 压缩公钥 +OP_PUSHBYTES_25 // 推送25字节到栈顶 +76a9148876d7c7a76e29c4c9d160c066fead1d2adfe5a788ac // 赎回脚本 +``` + +## 执行过程 + +1. 脚本合并,ScriptSig在前ScriptPubKey在后 + +```js + OP_HASH160 OP_EQUAL + +其中 +redeemScript = OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG + +scriptHash = hash160(OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG) +``` + +2. 栈内执行 + +- 提取和验证`redeemScript`: + 首先,从`ScriptSig`中提取`redeemScript`。 + 然后,计算`redeemScript`的哈希值,并检查它是否与`ScriptPubKey`中的``相匹配。 + +- 执行`redeemScript`: + 如果`redeemScript`的哈希值验证通过,接下来的步骤是执行`redeemScript`本身。 + 这时,栈中应该已经有了签名``和公钥``。 + redeemScript首先复制公钥`OP_DUP`,计算公钥的哈希`OP_HASH160`,并与栈中的公钥哈希``进行比较`OP_EQUALVERIFY`。 + 如果公钥哈希匹配,最后一步是验证签名是否正确`OP_CHECKSIG`。这一步检查提供的签名是否是用相关的私钥对交易的其余部分进行了签名。 + +- 完成验证: + 如果以上步骤都成功,说明交易中的资金可以被花费,否则交易将被拒绝。 + +## 地址生成 + +P2SH-P2PKH地址生成遵循以下步骤,这些步骤与生成P2PKH地址的步骤相似,但涉及一些关键的不同之处,特别是在脚本的使用和版本字节的应用上。 + +### 步骤1: 创建并哈希化 `P2PKH` 脚本 + +1. **创建 `P2PKH` 脚本**: + - 构建一个典型的P2PKH脚本:`OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG`,其中``是公钥的RIPEMD-160哈希。 +2. **计算脚本的SHA-256哈希值**: + - 对整个脚本进行SHA-256哈希运算。 +3. **计算RIPEMD-160哈希值**: + - 接着,对SHA-256的结果再进行RIPEMD-160哈希运算。这个哈希值称为脚本哈希(Script Hash)。 + +### 步骤2: 添加版本字节 + +- **添加版本字节**: + - 在脚本哈希前添加版本字节(比特币主网络的P2SH地址的版本是0x05, 测试网是0xc4)。这有助于钱包软件识别和处理P2SH地址。 + +### 步骤3: 计算校验和 + +1. **双重SHA-256哈希**: + - 对带有版本字节的脚本哈希进行两次SHA-256哈希运算。 +2. **取前四个字节**: + - 从双重哈希的结果中取出前四个字节作为校验和。 + +### 步骤4: 生成地址 + +- **组合和Base58编码**: + - 将版本字节、脚本哈希以及校验和组合在一起,结构如下: + ```js + [version byte][script hash][checksum] + ``` + - 然后,对整个字节串进行Base58编码,生成最终的P2SH-P2PKH地址。 + +### 示例 + +假设有一个公钥,步骤如下: + +1. **公钥**: `04480b6822120e9022c3ee7cdb92394e32e82ec5bc8860c8888df6a9910537e90c75079726a2a8469936b43859d84c380583c3d0292409b21453ae962815090f8117883c2a3fd7571b12f34491809d48467dae4e2f162aef23de91e4532d0fc1e0c5` +2. **SHA-256哈希**:计算结果: `98a21cf747c0fcd80845afffa2260feb64aa7cc2b73ac681407b25229bad3bbc` +3. **RIPEMD-160哈希**:计算结果: `999ff7726530ed0d0a7eb3b7442c5143f643f638` +4. **创建并哈希化 P2PKH 脚本**: + 创建一个P2PKH脚本:OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG。 + 计算脚本哈希:将整个P2PKH脚本进行SHA-256哈希运算,然后再进行RIPEMD-160哈希运算,生成脚本哈希。 +5. **添加版本字节**:`05` + [RIPEMD-160哈希结果]。 +6. **计算校验和**:对上述结果执行两次SHA-256,取前四个字节。 +7. **Base58编码**:将最终字节串转换为Base58编码:`3NB2grHkCeer9o7Guvr6qCXCsDsjX3mTan` diff --git a/BTC/Basic/payment/P2TR.md b/BTC/Basic/payment/P2TR.md new file mode 100644 index 000000000..b457a6840 --- /dev/null +++ b/BTC/Basic/payment/P2TR.md @@ -0,0 +1,68 @@ +# 简介 + +[P2TR(Pay to Taproot)](../wallet/address.js#L45)是一种使用Taproot技术的比特币地址类型,由BIP 0341在2020年正式引入。Taproot是通过使用Schnorr签名和MAST(Merkelized Abstract Syntax Trees)来增强比特币的隐私性和效率。Taproot Output 是版本为 1 的隔离见证 Output,统一了这两种形式。P2TR 的 Output 的 scriptPubKey 字段是一样的,我们无法从 Output 的格式来得知这个 Output 是由 Schnorr 签名锁定(即 Key Path)还是由脚本锁定(即 Script Path),这样有更好的隐私。这种类型的地址支持更复杂的条件,同时在外部看起来与普通交易无异。P2TR地址通常使用Bech32m编码格式,以“bc1p”开始。 + +# 工作过程 + +## ScriptPubKey(锁定脚本) + +在P2TR交易中,锁定脚本包括: + +```js +// 模版 +OP_1 + + +OP_1 // 表示版本号,对于P2TR是1 +OP_PUSHBYTES_20 // 推送20字节到栈顶 +18e1fad25b2983d5dbb2e2b96e3ce756a69b3bc2 // tweaked-public-key +``` + +- `OP_1`:标识Taproot版本号,目前为1。 +- ``:是tweak公钥的X坐标,key path和script path在生成方式上不同,采用32字节的压缩格式。 + +## Witness(见证数据) + +解锁P2TR输出,需要在见证字段提供以下信息,具体取决于花费条件: + +```js +// 模版 +[signature] [control block] [script] + +01 // Witness 元素的个数 +40 // schnorr签名长度 +743bbb3df4e95df5e70c2a3e72dd9d05933a018ad1bbd4b700841824ed18bebbc54d1b4d03b252aaa5c373390c68ba119fdf70e0bab71694a4db0da3fe8fefcc // schnorr签名 +``` + +- `signature`:Schnorr签名。 +- `control block`:如果是通过脚本路径花费,包含与Taproot脚本路径相关的数据 +- `script`:如果是通过脚本路径花费,此处包含实际脚本。 + +## 执行过程 + +P2TR分为key path和script path,通过在花费 P2TR UTXO 时,Witness 中包含元素个数来区分,只包含一个元素签名的即为key path。签名算法可以参考[Schnorr](../signature/README.md#L105),P2TR的验签过称可以参考[taproot](https://aandds.com/blog/bitcoin-taproot.html) + +## 地址生成 + +P2TR地址包含key path和script path两种情况,目前常见的钱包支持基于用户公钥的key path地址,改地址的生成过程如下: + +### 步骤1: 获取用户公钥 + +获取用户公钥的`internalPubkey`坐标 `P` + $P = toXOnly(internalPubkey)$ + +### 步骤2: 计算扭曲公钥 + +$Q = P + t\*G = P + TaggedHash('TapTweak', P)G$ + +### 步骤3: Bech32m编码 + +使用Bech32m编码方案将曲公钥编码为一个P2TR地址。这种编码格式提供了比Bech32更好的错误校验能力。 + +### 示例 + +假设有一个公钥的X坐标,步骤如下: + +1. **公钥X坐标**: `2c3ee7cdb92394e32e82ec5bc8860c8888df6a9910537e90c75079726a2a8469` +2. **扭曲公钥X坐标**: `84d67b14669b9eccf5fa76ac48294527e44e829a4534f0e695061e5a1a5a5c20` +3. **Bech32m编码**:将扭曲公钥X坐标转换为Bech32m编码:`bc1psnt8k9rxnw0vea06w6kys229yljyaq56g560pe54qc095xj6tssq3fpwfp` diff --git a/BTC/Basic/payment/P2WPKH.md b/BTC/Basic/payment/P2WPKH.md new file mode 100644 index 000000000..f8884e9a6 --- /dev/null +++ b/BTC/Basic/payment/P2WPKH.md @@ -0,0 +1,71 @@ +# 简介 + +[P2WPKH(Pay to Witness Public Key Hash)](../wallet/address.js#L44)是一种新型地址格式,它是隔离见证(SegWit)的一部分,由比特币改进提案BIP 0141在2017年引入。这种地址类型主要目的是减小交易的大小,从而降低手续费,同时增加交易的修改抵抗力(malleability resistance)。`P2WPKH`简化了标准的`P2PKH`地址脚本,将签名和公钥信息从交易的脚本部分移至见证部分,这样做有助于更高效的签名验证和交易大小的减小。`P2WPKH`地址通常以“bc1”开始,使用Bech32编码格式,与传统的`P2PKH`和`P2SH`地址格式(分别以“1”和“3”开始)显著不同。 + +# 工作过程 + +## ScriptPubKey(锁定脚本) + +在P2WPKH交易中,锁定脚本变得非常简洁: + +```js +// 模版 +OP_0 + +OP_0 // 表示版本号,对于P2WPKH是0 +OP_PUSHBYTES_20 // 推送20字节到栈顶 +18e1fad25b2983d5dbb2e2b96e3ce756a69b3bc2 // 公钥哈希 +``` + +- `OP_0`:表示版本号,对于P2WPKH是0。 +- ``:是公钥的SHA-256哈希后再进行RIPEMD-160哈希的结果,和传统P2PKH的处理方式相同。 + +## Witness(见证数据) + +为了解锁P2WPKH输出,需要在见证字段提供以下信息: + +```js +// 模版 + + +02 // 见证数据的数量 +48 // 第一个见证数据的长度72字节 +3045022100a5eec95c65d2dd679e8fdcc10668d5d51446f24d1557859cb543e570201257c6022011df04f803a08c97de213ddf211ad193045ff25e59857324a28656de1961351c01 // 签名数据 +21 // 第二个见证数据的长度33字节 +0295f97f41d0d523bddf39e77e16cc0dda2e56b9bd9fdea10133656635b2c28a39 // 压缩公钥 +``` + +## 执行过程 + +1. 脚本合并同P2PKH,使用Witness替换ScriptSig在前,ScriptPubKey在后 + +```js + OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG +``` + +2. 栈内执行通P2PKH + +- OP_DUP 从栈里取出公钥,复制并做HASH160推进栈顶 +- OP_EQUALVERIFY 对比上一步的计算结果和Public Key Hash +- OP_CHECKSIG 校验数字签名,如果通过锁定脚本合法可以花费对应UTXO,如果不通过交易失败 + +## 地址生成 + +P2WPKH地址的生成过程如下: + +### 步骤1: 计算公钥哈希 + +- 使用公钥生成哈希:首先对公钥进行SHA-256,然后进行RIPEMD-160哈希运算。 + +### 步骤2: Bech32编码 + +- 使用Bech32编码方案将公钥哈希编码为一个P2WPKH地址。这包括将数据与校验和一起编码,提供了更好的错误检测能力。 + +### 示例 + +假设有一个公钥,步骤如下: + +1. **公钥**: `022c3ee7cdb92394e32e82ec5bc8860c8888df6a9910537e90c75079726a2a8469` +2. **SHA-256哈希**:计算结果: `98a21cf747c0fcd80845afffa2260feb64aa7cc2b73ac681407b25229bad3bbc` +3. **RIPEMD-160哈希**:计算结果: `999ff7726530ed0d0a7eb3b7442c5143f643f638` +4. **Bech32编码**:将最终字节串转换为Bech32编码:`bc1qnx0lwun9xrks6zn7kwm5gtz3g0my8a3cqde85n` diff --git a/BTC/Basic/payment/README.md b/BTC/Basic/payment/README.md new file mode 100644 index 000000000..962955520 --- /dev/null +++ b/BTC/Basic/payment/README.md @@ -0,0 +1,38 @@ +# BTC地址 + +在比特币系统中,随着技术的发展和用户需求的变化,钱包现在支持多种不同类型的地址格式,每种格式都对应不同的技术标准和特点。这些地址格式包括传统的P2PKH(以1开头),P2SH(以3开头),以及更加现代的SegWit地址(通常以bc1q或bc1p开头)。进行交易时,我们需要为收款者创建一个交易输出,这个输出指定了收款方的地址和交易的金额。 + +# BTC地址到交易完成 + +下面以P2PKH(Pay-to-Public-Key-Hash)为例介绍从比特币收款地址到交易构建的详细步骤: + +1. 收款地址的获取和格式 + 收款方提供一个P2PKH类型的比特币地址,这种地址通常以数字1开始,例如 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2。这个地址是经过编码的,通常使用Base58Check编码,其中包含了收款方的公钥哈希(public key hash)。 + +2. Base58Check解码 + 当付款方接收到一个比特币地址后,他们会使用Base58Check解码来提取地址中的公钥哈希。Base58Check编码不仅帮助减少字符混淆(移除了容易混淆的字符如0(零)、O(大写字母O)、I(大写字母i)和l(小写字母L)),还内嵌了一个错误检测机制。 + +3. 构建锁定脚本(ScriptPubKey) + 解码过程提取出的公钥哈希接着用来构建一个所谓的锁定脚本(ScriptPubKey),这个脚本是交易输出的一部分。对于P2PKH地址,锁定脚本的格式通常如下: + +```js +OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG +``` + +4. 构建交易 + 付款方将锁定脚本放置在新的交易输出中,并指定转账金额。这个输出现在会被添加到新的交易中。除此之外,交易还需要包含至少一个输入(来自付款方的一个或多个以前的交易输出),这些输入提供了足够的资金来覆盖转账金额和网络矿工费。 + +5. 签名和广播交易 + 一旦交易构建完毕,付款方需要用他们的私钥对交易进行签名,证明他们有权使用指定的输入资金。完成签名后,交易将被广播到比特币网络,网络的矿工们开始验证这个交易的合法性,如果合法,最终将其加入到区块链中,完成资金的转移。 + +# 常见支付方式 + +| 支付方式 | 全称 | 地址前缀 | 脚本类型 | 主要特点 | 示例地址 | +|----------|-------------------------|----------|----------|-------------------------------------------------|---------------| +| [P2PKH](./P2PKH.md) | Pay-to-Public-Key-Hash | 1 | P2PKH | 最常用,公钥哈希方式,隐私性好,地址以 "1" 开头 | 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa | +| [P2SH](./P2SH-P2PKH.md) | Pay-to-Script-Hash | 3 | P2SH | 支付到脚本的哈希值,支持复杂的脚本,地址以 "3" 开头 | 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy | +| [P2WPKH](./P2WPKH.md) | Pay-to-Witness-Public-Key-Hash | bc1q | P2WPKH | 使用隔离见证(SegWit),更低交易费用,地址以 "bc1q" 开头 | bc1qar0srrr7xf6h4g1ks7zft6wz7x4n7pkjkcn6lq | +| P2WSH | Pay-to-Witness-Script-Hash | bc1q | P2WSH | 使用隔离见证,支持复杂脚本,地址以 "bc1q" 开头 | bc1qrp33g0q0s6fphxtww9g4ndzsd74ny3rps6m74dxg7mjsg5c0z9rhdj | +| [P2TR](./P2TR.md) | Pay-to-Taproot | bc1p | P2TR | 使用 Taproot 功能,优化脚本执行和隐私,地址以 "bc1p" 开头 | bc1p4gr63h6fp9hrkwpf4sjwx4s34a5w6b6ptfhl5k7vph3ykq6s5h7gx5m | + + diff --git a/BTC/Basic/signature/README.md b/BTC/Basic/signature/README.md new file mode 100644 index 000000000..570236925 --- /dev/null +++ b/BTC/Basic/signature/README.md @@ -0,0 +1,167 @@ +# 比特币中的数字签名 + +## ECDSA +比特币(BTC)在设计初期使用椭圆曲线数字签名算法(ECDSA)来保证交易的安全性。ECDSA在比特币中的应用提供了强大的安全性,保证交易不可篡改且可验证。它是确保比特币网络信任和安全的关键技术之一。 + +### 椭圆曲线密码学基础 +椭圆曲线定义:在比特币中,通常使用的是secp256k1椭圆曲线。这条曲线在一个有限域上定义,具有一定的形状,能够提供高强度的加密保护。 +公钥和私钥:私钥是一个随机选定的数,而公钥是由私钥通过椭圆曲线的数学运算得出的点。 + +### 生成签名的过程 +使用私钥创建签名:签名是使用交易发起者的私钥生成的。这个过程包括对交易的哈希值进行加密处理。 +签名组成:ECDSA签名由两部分组成,通常表示为(r, s)。 +r:是椭圆曲线点的x坐标。 +s:是对交易信息、私钥和一个随机数的数学运算的结果。 + +### 验证签名的过程 +使用公钥验证签名:接收方或网络节点使用发送方的公钥来验证签名。如果签名是有效的,这表明交易是由拥有相应私钥的人发起的,从而保证了交易的真实性和不可否认性。 +确保安全性:只有创建签名的私钥持有者才能生成有效的签名,而计算私钥本身几乎是不可能的,因为这需要在有限的时间内解决非常复杂的数学问题。 + +### 比特币中的应用 +交易认证:比特币交易使用ECDSA签名来验证交易的合法性。每笔交易都包括至少一个输入(来源资金)和一个输出(目的地资金)。 +防止篡改:ECDSA签名保证了一旦交易被签名,任何对交易内容的修改都会使签名失效,从而防止了交易的篡改。 + +椭圆曲线密码学提供了比传统的RSA算法更高的安全性,这意味着即使使用更短的密钥,也能提供同等甚至更高的安全性。 + +### 数学公式 +#### 密钥生成 (genKey) +1. 随机产生私钥 $d$,选点 $G$。 +2. 计算公钥 $Q = d \* G $ + +注意事项: $G$点阶数 $n$,但是根据椭圆曲线随机选点 $G$却是计算出来难点。 + +#### 加密 (encrypt) +1. 选择随机数r的点 $k = r \* G $,随机数r。 +2. 计算密文: $C_1 = M + r \* Q $, $C_2 = r \* G $ + +#### 解密 (decrypt) +1. $C_1 - d \* C_2 = M + r \* Q - r(d \* G) = M + r \* Q - r \* Q = M $ + +#### 签名 (sign) +1. 对消息m使用哈希算得摘要z,摘要 $z = hash(m)$ +2. 生成随机数 $k ∈ [1,n)$,计算点 $k = r \* G $ +3. 摘要 $z$, $R = k $,计算 $s = k^{-1}(z + rd) \mod n $,签名为 (r, s) +4. 计算 $s = k^{-1}(z + rd) \mod n $,签名 = (r, s) 即是进行授权协议 +5. 以上 $(r, s)$ 即为ECDSA签名 + +#### 验证 (verify) +使用公钥Q和消息m,对签名 $(r, s)$ 进行验证。 + +1. 验证 $r, s ∈ [1, n)$ +2. 计算 $z = hash(m)$ +3. 计算 $u_1 = z \* s^{-1} \mod n$ 和 $u_2 = r \* s^{-1} \mod n $ +4. 计算 $(x, y) = u_1 \* G + u_2 \* Q \mod n$ +5. 判断 $r == x$,若相等则验证签名正确 + +#### 恢复 (recover) +已知消息m和签名 $(r, s)$,恢复计算出公钥 $Q$ + +1. 验证 $r, s ∈ [1, n)$ +2. 计算 $R = (x, y)$,其中 $x = r, y + r, y + nr, y + 2nr, ...,$ 代入R到曲线方程计算得到R +3. 计算 $z = hash(m)$ +4. 计算 $u_1 = z \* r^{-1} \mod n$ 和 $u_2 = s \* r^{-1} \mod n$ +5. 计算公钥 $Q = (x', y') = u_1 \* G + u_2 \* R $ + +### 签名分析 +#### 签名内容 +对于使用ECDSA的比特币交易,签名通常包括两部分:r 和 s,每部分通常是32字节,但也可能因数字的大小而稍有变化。因此,一个典型的ECDSA签名大约是64字节,但当包括签名编码和额外的元数据(如签名哈希类型)时,长度可能会变长。 +#### 签名的具体长度 +具体到比特币的签名,它通常包括以下几个部分: +- r值:32字节 +- s值:32字节 +- 签名哈希类型(SIGHASH):通常是1字节 +此外,签名还可能包括序列化格式的前缀(如0x30),以及长度描述符,这些都会增加几个字节。 + +#### 签名案例 +```js +// 签名 +3045022100c233c3a8a510e03ad18b0a24694ef00c78101bfd5ac075b8c1037952ce26e91e02205aa5f8f88f29bb4ad5808ebc12abfd26bd791256f367b04c6d955f01f28a772401 +``` + +##### DER编码的ECDSA签名结构 +一个典型的ECDSA签名的DER编码格式如下: +- 0x30: 表示这是一个序列(SEQUENCE),后面跟随的是整个序列的长度。 +- 紧接着的第一个字节(或两个字节,取决于长度)是整个签名结构的长度。 +- 0x02: 表示一个整数(INTEGER)的开始,这是签名的第一部分,即r值。 +- 紧接着的一个字节(或两个字节)是r值的长度。 +- r值本身。 +- 另一个0x02,表示第二个整数的开始,这是签名的第二部分,即s值。 +- 紧接着的一个字节(或两个字节)是s值的长度。 +- s值本身。 +##### 分析具体内容 +1. 30: 序列开始。 +2. 45: 表示序列的长度(十进制的69字节)。 +3. 02: r值的整数开始。 +4. 21: r值的长度(十进制的33字节,包含前导0,因为r值开始于00,这通常用于表示正数以避免整数被误认为负数)。 +5. 00c233c3a8a510e03ad18b0a24694ef00c78101bfd5ac075b8c1037952ce26e91e: r值。 +6. 02: s值的整数开始。 +7. 20: s值的长度(十进制的32字节)。 +8. 5aa5f8f88f29bb4ad5808ebc12abfd26bd791256f367b04c6d955f01f28a7724: s值。 +9. 最后的01是签名的SIGHASH类型,表示这是一个标准的所有输出签名。 + +## Schnorr +比特币的Schnorr签名是一种相对较新的签名算法,它与传统的椭圆曲线数字签名算法(ECDSA)相比,提供了几个关键优势。在2020年的比特币协议升级中,Schnorr签名通过BIP340提案得到了引入。以下是Schnorr签名的详细介绍: + +### Schnorr签名概述 +发明者:Schnorr签名由Claus Schnorr提出。 +算法特性:Schnorr签名基于椭圆曲线密码学,与ECDSA使用相同的椭圆曲线。 +简洁性和效率:Schnorr签名比ECDSA更简洁、高效。它生成的签名更小,处理速度更快。 + +### Schnorr签名的优势 +线性性:Schnorr签名具有线性特性,使得多签名(multi-signature)操作更简单有效。这意味着可以将多个签名组合成一个单一签名,从而减少数据的大小和处理时间。 +隐私和安全性:Schnorr签名提升了隐私和安全性。它使得各种复杂的交易类型(如多签名交易)在区块链上看起来与普通交易无异,从而提高了隐私性。 +防止重放攻击:Schnorr签名包含额外的措施来防止重放攻击,这是ECDSA所缺乏的。 + +### 比特币中的应用 +简化多签名:Schnorr签名简化了比特币网络中的多签名交易处理,减少了数据量和验证所需的时间。 +批量验证:Schnorr签名支持批量验证,允许网络同时验证多个签名,提高了整体效率。 +Taproot升级:结合Schnorr签名的Taproot升级为比特币引入了更高的效率和隐私性。 + +### 签名过程 +私钥和公钥:与ECDSA一样,Schnorr签名使用一对私钥和公钥。 +签名生成:签名是使用私钥和交易的哈希值生成的。Schnorr签名的计算相对简单,它包括了一些线性数学运算。 + +### 全性和效率 +安全性:Schnorr签名被认为至少与ECDSA同等安全,有些专家认为其安全性更高。 +效率和可扩展性:签名的大小和验证的效率对于比特币网络的可扩展性至关重要。Schnorr签名在这方面表现优异。 + +Schnorr签名在比特币中的引入被视为一个重大进步,它提高了交易的隐私性、效率和可扩展性。随着时间的推移,预计Schnorr签名将在比特币网络中发挥越来越重要的作用。 + +### 数学公式 +#### 密钥生成 (genKey) +1. 在群的阶 $n$ 内随机选择一个私钥 $d$,通常从 $[1, n)$ 区间选择。 +2. 计算公钥 $Q = d \cdot G$,其中 $G$ 是椭圆曲线上的基点。 + +#### 签名 (sign) +1. 从 $[1, n)$ 区间内随机选取一个数 $k$。 +2. 计算 $r = (k \cdot G).x$(即 $k \cdot G$ 点的x坐标)。 +3. 计算签名 $s = (k + d \cdot \text{hash}(m)) \mod n$,这里 $\text{hash}(m)$ 是消息 $m$ 的哈希值。 +4. 签名结果是一个元组 $(r, s)$。 + +#### 验证 (verify) +1. 计算 $u1 = \text{hash}(m) \cdot s^{-1} \mod n$。 +2. 计算 $u2 = r \cdot s^{-1} \mod n$。 +3. 计算点 $R' = u1 \cdot G + u2 \cdot Q$。 +4. 验证 $r$ 是否等于 $R'$ 的x坐标,如果相等,则签名有效。 + +#### 恢复 (recover) +假设我们有一个有效的Schnorr签名 $(r, s)$ 和消息 $m$,以下是计算公钥的步骤: +1. 计算消息的哈希值 $e = \text{hash}(m)$。 +2. 计算签名 $s = k + e \cdot d$。 +3. 进行 $k \cdot G$ 运算得到 $s \cdot G = k \cdot G + e \cdot d \cdot G$ 即 $s \cdot G = R + e \cdot Q$。 +4. 最后,公钥 $Q = (s \cdot G - R) / e$ 计算出来。 +5. 其中 $r$ 是 $R$ 的x坐标,可以计算得到 $R$。 + + + + +## ECDSA VS Schnorr + +| 特性 | ECDSA | Schnorr | +|------------|-------------------------------------------|----------------------------------------------| +| 安全性 | 基于椭圆曲线离散对数问题的困难性。随机数重用或泄露会暴露私钥。 | 基于椭圆曲线离散对数问题的困难性。对随机数的选择不敏感,即使随机数可预测,也相对安全。 | +| 效率 | 签名和验证相对复杂,计算量大。 | 签名和验证过程更高效,特别是在批量操作时。 | +| 可组合性 | 无签名可组合性。 | 支持签名聚合,可将多个签名合并为一个。 | +| 签名大小 | 包含两个等长的整数`(r, s)`。 | 同样包含`(r, s)`对,但因可组合性,最终可能存储更少的签名数据。 | +| 隐私与可证明性 | 无额外隐私保护或可证明性。 | 签名可组合性可用于增强隐私和提供非交互式的多方协议。 | +| 实际应用 | 在众多标准和协议中广泛使用,如比特币和以太坊早期版本。 | 由于技术发展被越来越多的系统采纳,例如比特币的BIP 340提案。 | diff --git a/BTC/Basic/signature/ecdsa.js b/BTC/Basic/signature/ecdsa.js new file mode 100644 index 000000000..8d56540d3 --- /dev/null +++ b/BTC/Basic/signature/ecdsa.js @@ -0,0 +1,29 @@ +import ecpair from 'ecpair'; +import * as tinysecp from 'tiny-secp256k1'; +import crypto from 'crypto'; + +/// In this example, we first generate a new private key and corresponding public key. +/// We then create a simulated transaction hash (in a real application, this would be the actual hash of the transaction) and use the private key to sign this hash. +/// Finally, we verify that the signature is valid. + +const { ECPairFactory } = ecpair; +const ECPair = ECPairFactory(tinysecp); + +// Generate a new random private key +const keyPair = ECPair.makeRandom(); +const privateKey = keyPair.privateKey; +const publicKey = keyPair.publicKey; + +console.log('private key:', privateKey.toString('hex')); +console.log('public key:', publicKey.toString('hex')); + +// Create a hypothetical transaction hash, usually this will be the hash of the real transaction you want to sign +const txHash = crypto.randomBytes(32); + +// signature transaction +const signature = keyPair.sign(txHash); +console.log('signature:', signature.toString('hex')); + +// verify transaction +const isValid = keyPair.verify(txHash, signature); +console.log('signature is valid:', isValid); diff --git a/BTC/Basic/signature/ecdsaPSBT.js b/BTC/Basic/signature/ecdsaPSBT.js new file mode 100644 index 000000000..8b6d0e346 --- /dev/null +++ b/BTC/Basic/signature/ecdsaPSBT.js @@ -0,0 +1,46 @@ +import ecpair from 'ecpair'; +import * as tinysecp from 'tiny-secp256k1'; +import bitcoin from 'bitcoinjs-lib'; + +/// In this example, We get keyPair from WIF. +/// We then construct a PSBT with one input (which you're spending) and one output (where you're sending bitcoins). +/// The PSBT is signed using the private key. +/// Finally, we build the PSBT and print its hexadecimal representation. + +const { ECPairFactory } = ecpair; +const ECPair = ECPairFactory(tinysecp); + +// Get keyPair from WIF +const keyPair = ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr'); + +// Create a new PSBT with the first input of the transaction +let psbt = new bitcoin.Psbt() + .addInput({ + hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e', + index: 0, + nonWitnessUtxo: Buffer.from( + '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' + + '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' + + 'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' + + '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' + + '631e5e1e66009ce3710ceea5b1ad13ffffffff01905f0100000000001976a9148bb' + + 'c95d2709c71607c60ee3f097c1217482f518d88ac00000000', + 'hex' + ), + }) + .addOutput({ + address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp', + value: 80000, + }); + +// Sign the first input with the key +psbt.signInput(0, keyPair); + +// Finalize the inputs +psbt.finalizeAllInputs(); + +// Extract the transaction +const rawTx = psbt.extractTransaction().toHex(); + +// Print the transaction +console.log(`Raw Transaction: ${rawTx}`); diff --git a/BTC/Basic/signature/ecdsaTransaction.js b/BTC/Basic/signature/ecdsaTransaction.js new file mode 100644 index 000000000..15359e2ba --- /dev/null +++ b/BTC/Basic/signature/ecdsaTransaction.js @@ -0,0 +1,42 @@ +import ecpair from 'ecpair'; +import * as tinysecp from 'tiny-secp256k1'; +import bitcoin from 'bitcoinjs-lib'; + +/// In this example, We create a new private key and its corresponding public key. +/// We then construct a transaction with one input (which you're spending) and one output (where you're sending bitcoins). +/// The transaction is signed using the private key. +/// Finally, we build the transaction and print its hexadecimal representation. + +const { ECPairFactory } = ecpair; +const ECPair = ECPairFactory(tinysecp); + +// Securely generate and store the private key. Here, we use a randomly generated private key for demonstration. +const keyPair = ECPair.makeRandom(); +const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }); +const privateKey = keyPair.privateKey.toString('hex'); + +console.log(`Address: ${address}`); +console.log(`Private Key: ${privateKey}`); + +// Replace the following with a real transaction hash and the corresponding index of the output you want to spend. +const txHash = Buffer.from('c8c5b5a17a07c7cecdbdece070843fb074ae039400a26c27861927dab26631b5', 'hex'); // This should be a real transaction hash +const txId = 1; // Typically 0 or 1 + +// Create a new transaction builder +const transaction = new bitcoin.Transaction(); + +// Add the input (the transaction hash and index) +transaction.addInput(txHash, txId); + +// Add the output (recipient address and amount in satoshis) +transaction.addOutput(Buffer.alloc(0), 10000); // The amount you want to send, in satoshis + +// Sign the transaction with the private key of the input +const hash = transaction.hashForSignature(0, Buffer.from('001494fea8dd42d30e583fdf39537fb7e2ee0533e6b7', 'hex'), 0); +const signature = keyPair.sign(hash); +transaction.setInputScript(0, bitcoin.script.compile([signature, keyPair.publicKey])); + +// Build the transaction and get its hex representation +const tx = transaction.toHex(); + +console.log(`Transaction: ${tx}`); diff --git a/BTC/Basic/signature/package.json b/BTC/Basic/signature/package.json new file mode 100644 index 000000000..bf4ec8502 --- /dev/null +++ b/BTC/Basic/signature/package.json @@ -0,0 +1,17 @@ +{ + "name": "btc-wallet", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "bitcoinjs-lib": "^6.1.5", + "ecpair": "^2.1.0", + "tiny-secp256k1": "^2.2.3" + } +} diff --git a/BTC/Basic/signature/schnorr.js b/BTC/Basic/signature/schnorr.js new file mode 100644 index 000000000..e6f24db93 --- /dev/null +++ b/BTC/Basic/signature/schnorr.js @@ -0,0 +1,30 @@ +import ecpair from 'ecpair'; +import * as tinysecp from 'tiny-secp256k1'; +import crypto from 'crypto'; + +/// In this example, we first generate a new private key and corresponding public key. +/// We create a dummy transaction hash. In a real scenario, this would be the hash of your transaction data. +/// We generate a Schnorr signature using the private key and the transaction hash. +/// Finally, we verify that the signature is valid. + +const { ECPairFactory } = ecpair; +const ECPair = ECPairFactory(tinysecp); + +// Generate a new random private key +const keyPair = ECPair.makeRandom(); +const privateKey = keyPair.privateKey; +const publicKey = keyPair.publicKey; + +console.log('private key:', privateKey.toString('hex')); +console.log('public key:', publicKey.toString('hex')); + +// Create a hypothetical transaction hash, usually this will be the hash of the real transaction you want to sign +const txHash = crypto.randomBytes(32); + +// signature transaction +const signature = keyPair.signSchnorr(txHash); +console.log('Schnorr Signature:', signature.toString('hex')); + +// verify transaction +const isValid = keyPair.verifySchnorr(txHash, signature); +console.log('signature is valid:', isValid); diff --git a/BTC/Basic/tools/README.md b/BTC/Basic/tools/README.md new file mode 100644 index 000000000..d0668975e --- /dev/null +++ b/BTC/Basic/tools/README.md @@ -0,0 +1,26 @@ +# BTC工具目录 + +欢迎访问比特币工具目录!随着比特币和其他加密货币的流行,对于普通用户而言,理解其背后的技术变得越来越重要。本目录旨在提供一系列易于使用的工具,帮助您深入了解比特币的基本原理、操作和技术细节。 + + +## 可用工具 + +### [dust计算](./dust.md) + +- **描述**:在比特币网络中,“尘埃”(Dust)是一个专有术语,它指的是一种非常小的比特币金额,这种金额小到它的交易费用可能会超过这笔金额本身的价值。因此,从经济角度来看,发送这样一笔交易是不划算的。 +- **影响**:尘埃限制的存在主要是为了防止网络被大量的微小交易所拥堵,这些交易可能是无效的或试图进行“尘埃攻击”(发送大量的小额交易到多个地址,以试图追踪这些地址的所有者)。因此,比特币网络通过设置尘埃限制来保护网络不被这种小额交易滥用。 + +### [base58 编码/解码](./base58.md) + +- **描述**:Base58是一种二进制到文本的编码方式,主要用于编码比特币地址。 +- **使用方法**:Base58编码主要用于生成人类可读的公钥哈希(即比特币地址)和私钥的可导入格式。这样的格式不仅容易被人识别和处理,也方便在不同的应用和服务之间进行交易和管理。 + +### [Bech32 编码/解码](./bech32.md) + +- **描述**:Bech32 是一种字符编码方案,设计用于提高比特币地址的可读性和纠错能力。它是一种人类可读的编码方式,主要用于比特币隔离见证(SegWit)地址的表示。 +- **使用方法**:Bech32 编码用于生成比特币隔离见证地址(例如,P2WPKH 和 P2WSH 地址)。它采用了一个包含 32 个字符的字符集,使得地址既易于手动输入,又具有较强的错误检测能力。Bech32 地址以“bc1”开头,后跟若干字符,提供了更高的错误检测能力和更好的用户体验。 + +### [Bech32m 编码/解码](./bech32m.md) + +- **描述**:Bech32m 是 Bech32 的一种变体,专门用于 Taproot 地址的编码。它与 Bech32 在结构上类似,但引入了不同的错误检测机制,以增强对 Taproot 地址的支持。 +- **使用方法**:Bech32m 编码用于生成 Taproot 地址,这种地址以“bc1p”开头。Bech32m 提供了改进的错误检测能力,适合用于 Taproot 地址的使用。它在 Bech32 基础上增加了一些功能,确保 Taproot 地址具有更好的数据完整性和错误校验能力。 \ No newline at end of file diff --git a/BTC/Basic/tools/base58.md b/BTC/Basic/tools/base58.md new file mode 100644 index 000000000..f84aeb825 --- /dev/null +++ b/BTC/Basic/tools/base58.md @@ -0,0 +1,91 @@ +Base58 是一种用于将大数字转换为可读的文本格式的编码方案,广泛用于比特币以及其他多种加密货币的地址。与常见的 Base64 编码类似,Base58 是一种二进制到文本的编码,设计它的主要目的是为了减少混淆并增加地址的可读性。 + +### 特点 + +Base58 编码具有几个显著的特点,使其适合用于加密货币地址: + +1. **去除视觉上易混淆的字符**:Base58编码不使用数字"0",大写字母"O",大写字母"I",和小写字母"l",因为这些字符在视觉上容易与其他字符混淆。 +2. **无标点符号**:Base58不包含标点符号,这减少了在抄写或读取地址时出错的可能性。 +3. **编码效率**:Base58 编码是紧凑的,它使得编码的数据相对较短,便于分享和书写。 +4. **大小写敏感**:Base58是大小写敏感的,增加了编码的字符集,从而提高了每个字符的信息容量。 + +### Base58 字符集 + +Base58使用以下58个字符作为其字母表: + +``` +123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz +``` + +注意这里没有0, O, I, l,这是为了避免在读写时的混淆。 + +### 应用 + +在比特币和许多其他加密货币中,Base58编码主要用于生成人类可读的公钥哈希(即比特币地址)和私钥的可导入格式。这样的格式不仅容易被人识别和处理,也方便在不同的应用和服务之间进行交易和管理。 + +### 技术实现 + +Base58 编码涉及将大整数转换为一种基于58的数制系统。编码过程通常包括以下步骤: + +1. 将初始数据(例如,比特币地址的哈希值)转换为一个大整数。 +2. 将这个大整数重复除以58,同时记录每次除法的余数。 +3. 将余数用对应的Base58字符表示,直到整数部分为0。 +4. 将结果的字符反转,因为在计算过程中,第一个余数实际上对应最后一个Base58字符。 + +### 示例 + +##### 编码 + +```js +base10 = 123456789 + +123456789 % 58 = 19 <- remainder + 2128565 % 58 = 23 <- remainder + 36699 % 58 = 43 <- remainder + 632 % 58 = 52 <- remainder + 10 % 58 = 10 <- remainder + +base58 = [10][52][43][23][19] +base58 = BukQL +``` + +#### 解码 + +```js +base58 = BukQL + +L = 19 * 58^0 = 19 +Q = 23 * 58^1 = 1334 +k = 43 * 58^2 = 144652 +u = 52 * 58^3 = 10145824 +B = 10 * 58^4 = 113164960 + +base10 = 19 + 1334 + 144652 + 10145824 + 113164960 +base10 = 123456789 +``` + +### BTC中的应用 + +Base58 最初仅用于对BTC地址进行编码,在后来引入 WIF 私钥和扩展密钥时也使用Base58编码。 + +#### 前缀 + +在比特币中,在转换为 base58 之前,会向数据添加不同的前缀以影响结果的前导字符,这个前导字符有助于我们识别每个 base58 字符串代表什么。 + +| Hex Prefix | Base58首字符 | 类型 | 示例 | +| ---------- | ------------ | ------------ | --------- | +| 00 | 1 | 地址 (P2PKH) | 1mGqBH... | +| 05 | 3 | 地址 (P2SH) | 3JiUY4... | +| 80 | K, L, 或 5 | WIF私钥 | Lu38mr... | +| 0488ADE4 | xprv | 扩展私钥 | xprv9t... | +| 0488B21E | xpub | 扩展公钥 | xpub67... | + +#### base58 check + +Base58Check 编码是将某些数据编码为 base58 之前向某些数据添加校验和的缩写。 + +在BTC的base58编码时 + +1. 首先会对原始数据增加对应的prefix +2. 对上述结果执行两次SHA-256,取前四个字节做checkSum +3. 连接1和2的结果进行base58编码 diff --git a/BTC/Basic/tools/bech32.md b/BTC/Basic/tools/bech32.md new file mode 100644 index 000000000..74d733b46 --- /dev/null +++ b/BTC/Basic/tools/bech32.md @@ -0,0 +1,193 @@ +Bech32是一种编码格式,最初由Pieter Wuille等人在比特币改进提案[BIP 173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)中提出,主要用于比特币SegWit地址的编码方式。Bech32编码的目的是提供一种更友好的用户界面,并减少在抄写和输入时出现错误的风险。 + +### 核心特性 + +**1. 错误检测能力:** +Bech32地址包含一种错误检测代码,称为多项式校验码(PolyMod),可以检测并保护地址中最多4个错误的出现。 + +**2. 字符集:** +Bech32只使用小写字母和数字(除了1、b、i、o之外的小写字母和所有数字),从而避免了字母大小写的混淆和某些字母和数字之间的视觉相似性。 + +**3. 限制长度:** +一个Bech32编码的地址总是有一定的长度限制,比特币的SegWit地址最长为90个字符。 + +**4. 分隔符:** +Bech32地址中使用"1"作为分隔符来分开人类可读的部分(HRP,Human-readable part)和数据部分。 + +### 编码格式 + +Bech32地址的结构如下: + +\[ \text{HRP} + "1" + \text{数据编码} + \text{校验码} \] + +- **HRP(Human-readable part):** 这部分是地址的前缀,用来指示地址所用的网络,例如比特币主网的`bc`和测试网的`tb`。 +- **数据编码:** 使用的是一种叫做Base32的变种编码,将数据转换成32个字符集中的字符。 +- **校验码:** 是一组校验码字符,用于保护整个地址不受随机错误的影响。 + +Bech32是一种用于编码和解码比特币SegWit地址的格式,具体方法由[BIP 173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)定义。编码和解码过程涉及多个步骤,包括数据的处理、转换以及校验。 + +在实际项目中可以使用JavaScript的常用库bech32实现的Bech32编码和解码。 +**Bech32编码示例** + +```js +const bech32 = require('bech32'); + +function encodeBech32(hrp, data) { + const words = bech32.toWords(Buffer.from(data)); + return bech32.encode(hrp, words); +} + +// 示例使用 +let hrp = 'bc'; +let data = '00112233445566778899aabbccddeeff'; // 假设这是要编码的数据 +let encoded = encodeBech32(hrp, data); +console.log('Encoded Bech32:', encoded); +``` + +**Bech32解码示例** + +```js +function decodeBech32(address) { + let decoded = bech32.decode(address); + decoded.data = Buffer.from(bech32.fromWords(decoded.words)).toString('hex'); + return decoded; +} + +// 使用之前编码的地址进行解码 +let decoded = decodeBech32(encoded); +console.log('Decoded Bech32:', decoded); +``` + +### Bech32编码过程 + +编码过程涉及以下步骤: + +1. **确定人类可读部分(HRP)和数据部分:** + + - HRP表示网络,比如比特币主网的`bc`,测试网的`tb`。 + - 数据部分包含经过编码的公钥或脚本哈希。 + +2. **将数据部分从8位字节转换为5位数组:** + + - Bech32使用Base32编码,这不同于传统的Base32。数据需要从其原始的8位字节形式(如在二进制文件中)转换为5位字节的数组。这种转换减少了数据的比特长度,并允许整个地址适应QR码等格式。 + +3. **计算校验码:** + + - 使用`PolyMod`算法计算出一个校验码,该算法可以提供错误检测功能。校验码计算依赖于HRP和转换后的数据。 + - 校验码有助于保护整个地址不受随机错误的影响。 + +4. **组合最终的Bech32地址:** + - 最终的Bech32地址由三部分组成:HRP、分隔符(`1`)、编码后的数据和校验码。 + - 示例:`bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4` + +### Bech32解码过程 + +解码过程涉及以下步骤: + +1. **验证地址的校验码:** + + - 使用相同的`PolyMod`算法验证地址中的校验码是否正确。如果校验失败,则地址有错误。 + +2. **分离HRP和编码数据:** + + - 从地址中分离出HRP和编码后的数据部分。这两部分由分隔符`1`分开。 + +3. **从5位数组转换回8位字节:** + + - 将从Bech32地址中提取的5位数组数据转换回原始的8位字节形式。这通常涉及二进制数据的重新分组。 + +4. **解析数据部分以获取原始的哈希或密钥信息:** + - 解码后的数据通常是某种形式的哈希值或公钥信息,这取决于具体的应用场景(如P2WPKH或P2WSH)。 + +`PolyMod`是一种特别设计用于Bech32编码系统中的多项式模运算算法。在Bech32地址格式中,`PolyMod`算法用于生成一个校验码,该校验码具有高错误检测能力,能够帮助确认地址在传输或书写过程中是否被错误修改。此算法是Bech32提案(BIP 173)的核心部分,对于保证地址的完整性和准确性至关重要。 + +### PolyMod算法的作用 + +在Bech32中,`PolyMod`算法的主要作用是计算一个校验码,这个校验码能够: + +- 检测到任何单一的错误。 +- 检测到任何相邻字符的交换。 +- 检测到最多四个错误的错误组合。 + +### PolyMod算法的实现步骤 + +`PolyMod`算法的计算可以分为以下几个步骤: + +1. **初始化**:设置一个初始变量`c`为1。 + +2. **处理HRP**:对于HRP(人类可读部分)中的每个字符,进行以下操作: + + - 将`c`向右移动5位。 + - 将字符的值与`c`进行异或(XOR)运算。 + - 对`c`进行一系列的模2多项式运算,通常这涉及到与几个固定的常数进行XOR运算,这些常数由Bech32规范定义。 + +3. **处理数据部分**:类似地处理数据部分中的每个5位值,包括: + + - 将`c`向右移动5位。 + - 将数据值与`c`进行异或运算。 + - 对`c`进行类似上述的模2多项式运算。 + +4. **生成校验码**:完成所有字符的处理后,将`c`与一个常数进行XOR运算,得到最终的校验码。这一步确保了校验码能够有效地反映整个地址的完整性。 + +** 示例代码 ** + +```js +function polymod(values) { + let generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]; + let chk = 1; + for (let value of values) { + let top = chk >> 25; + chk = ((chk & 0x1ffffff) << 5) ^ value; + for (let i = 0; i < 5; i++) { + if ((top >> i) & 1) { + chk ^= generator[i]; + } + } + } + return chk; +} + +function hrpExpand(hrp) { + let ret = []; + for (let p = 0; p < hrp.length; p++) { + ret.push(hrp.charCodeAt(p) >> 5); + } + ret.push(0); + for (let p = 0; p < hrp.length; p++) { + ret.push(hrp.charCodeAt(p) & 31); + } + return ret; +} + +function createChecksum(hrp, data) { + const values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]); + const polymodValue = polymod(values) ^ 1; + let checksum = []; + for (let i = 0; i < 6; i++) { + checksum.push((polymodValue >> (5 * (5 - i))) & 31); + } + return checksum; +} + +// Example usage +let hrp = 'bc'; +let data = [2, 3, 4, 5, 6]; // Example data part (this should be your actual converted data) +let checksum = createChecksum(hrp, data); +console.log('Checksum:', checksum); +``` + +### 数学原理 + +`PolyMod`算法本质上是一个使用特定生成多项式的循环冗余校验(CRC)算法。这个生成多项式是经过选择的,能够最大化常见错误模式的检测能力,例如拼写错误、字符遗漏、错误插入等。 + +### 使用场景 + +Bech32地址主要用于比特币的SegWit交易,其中包括了P2WPKH(Pay to Witness Public Key Hash)和P2WSH(Pay to Witness Script Hash)类型的地址。这些地址类型通过减少交易大小来降低交易费用,同时提高交易的隐私性和效率。 + +### 优点 + +- **更高的错误检测能力:** Bech32的设计提供了强大的错误检测和纠正能力。 +- **用户友好:** 地址全小写,易于阅读和抄写。 +- **效率与兼容性:** 地址长度更短,适用于QR码等多种场景。 + +Bech32编码为比特币地址提供了一个更加安全和用户友好的格式,是比特币网络中SegWit技术的一个重要组成部分。 diff --git a/BTC/Basic/tools/bech32m.js b/BTC/Basic/tools/bech32m.js new file mode 100644 index 000000000..be7fd7e5e --- /dev/null +++ b/BTC/Basic/tools/bech32m.js @@ -0,0 +1,121 @@ +const CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; + +// 将字符转换为数值 +function charToValue(char) { + const index = CHARSET.indexOf(char); + if (index === -1) { + throw new Error('Invalid character'); + } + return index; +} + +// 将数值转换为字符 +function valueToChar(value) { + return CHARSET[value]; +} + +// 多项式模数计算 Bech32 校验和 +function polymod(values) { + let checksum = 1; + for (let value of values) { + const top = checksum >> 25; + checksum = ((checksum & 0x1ffffff) << 5) ^ value; + for (let i = 0; i < 5; i++) { + if ((top >> i) & 1) { + checksum ^= (0x3b6a57b2 >> (5 * i)) & 0x1ffffff; + } + } + } + return checksum; +} + +// 扩展人类可读部分(HRP, Human-Readable Part)以帮助计算校验和 +function hrpExpand(hrp) { + const ret = []; + // 先将每个字符转换为其ASCII码除以32的余数 + for (let i = 0; i < hrp.length; i++) { + ret.push(hrp.charCodeAt(i) >> 5); + } + // 添加分隔符 + ret.push(0); + // 再将每个字符转换为其ASCII码的低5位 + for (let i = 0; i < hrp.length; i++) { + ret.push(hrp.charCodeAt(i) & 31); + } + return ret; +} + +// 计算校验和的最终值(Bech32m) +function createChecksum(hrp, data) { + const values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]); + const polymodValue = polymod(values) ^ 0x2bc830a3; // Bech32m 标识多项式 + let result = []; + for (let i = 0; i < 6; ++i) { + result.push((polymodValue >> (5 * (5 - i))) & 31); + } + return result; +} + +// 验证校验和 +function verifyChecksum(hrp, data) { + const expandedHrp = hrpExpand(hrp); + const values = expandedHrp.concat(data); + return polymod(values) === 0x2bc830a3; // 对于 Bech32, 有效校验和会返回1, 对于 Bech32m 需要验证不同的常量 +} + +function convertBits(data, fromBits, toBits, pad = true) { + let acc = 0; + let bits = 0; + const ret = []; + const maxv = (1 << toBits) - 1; + for (let i = 0; i < data.length; ++i) { + let value = data[i]; + if (value < 0 || value >> fromBits !== 0) { + return null; + } + acc = (acc << fromBits) | value; + bits += fromBits; + while (bits >= toBits) { + bits -= toBits; + ret.push((acc >> bits) & maxv); + } + } + if (pad) { + if (bits > 0) { + ret.push((acc << (toBits - bits)) & maxv); + } + } else if (bits >= fromBits || (acc << (toBits - bits)) & maxv) { + return null; + } + return ret; +} + +function encodeBech32m(hrp, data) { + const combined = data.concat(createChecksum(hrp, data)); + let result = hrp + '1'; + for (let datum of combined) { + result += valueToChar(datum); + } + return result; +} + +function decodeBech32m(bech32mStr) { + const pos = bech32mStr.lastIndexOf('1'); + const hrp = bech32mStr.slice(0, pos); + const data = []; + for (let i = pos + 1; i < bech32mStr.length; ++i) { + data.push(charToValue(bech32mStr[i])); + } + if (!verifyChecksum(hrp, data)) { + throw new Error('Invalid checksum'); + } + return { hrp, data: data.slice(0, data.length - 6) }; +} + +const hrp = 'bc'; +const data = [15, 2, 20, 3, 5, 18]; // 随机示例数据 +const encoded = encodeBech32m(hrp, data); +console.log('Encoded:', encoded); + +const decoded = decodeBech32m(encoded); +console.log('Decoded:', decoded); diff --git a/BTC/Basic/tools/bech32m.md b/BTC/Basic/tools/bech32m.md new file mode 100644 index 000000000..7962fb6e9 --- /dev/null +++ b/BTC/Basic/tools/bech32m.md @@ -0,0 +1,325 @@ +# Bech32m + +`Bech32m` 是一种新的编码格式,用于比特币地址。它是 `Bech32` 格式的修改版本,主要用于支持 SegWit v1+ 的比特币地址(例如 Taproot 地址)。`Bech32m` 是为了解决原始 `Bech32` 编码中发现的一个错误而被提出和实现的。 + +## 简介 + +### 背景 + +`Bech32` 格式最初是在 [BIP 173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) 中提出的,用于编码 SegWit(隔离见证)地址,这种格式的地址以 `bc1` 开头。然而,当 `Bech32` 用于新的 SegWit 版本时(即版本1及以上),由于错误检测机制存在问题,导致部分无效地址不会被正确地检测出来。 + +### Bech32m 的改进 + +为了解决这个问题,[BIP 350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki) 提出了 `Bech32m`,这是一个修改后的 `Bech32` 格式。它引入了一个新的校验和算法,提高了错误检测的能力,特别是适用于 SegWit v1 及更高版本的地址。这确保了编码的健壮性和安全性,尤其是在处理高版本的 SegWit 地址时。 + +### Bech32 与 Bech32m 的区别 + +主要区别在于校验和的计算方式。`Bech32` 使用的是一个固定的常数,而 `Bech32m` 使用了一个不同的常数来计算校验和。这一改变使得 `Bech32m` 能够更有效地捕捉到一些特定的错误,这些错误在原始的 `Bech32` 格式中可能无法检测到。 + +| 特性 | Bech32m | Bech32 | Base58 | +| -------------------- | ---------------------------- | -------------------------------- | ------------------------------ | +| **描述** | 改进的 SegWit 地址格式 | SegWit 地址格式 | 传统的比特币地址格式 | +| **BIP** | BIP 350 | BIP 173 | 无特定BIP,但参考 BIP 13/16 | +| **错误检测** | 改进的错误检测能力 | 强错误检测能力 | 较弱的错误检测能力 | +| **字符集** | 32字符集,不包括易混淆字符 | 32字符集,不包括易混淆字符 | 58字符集 | +| **地址前缀** | `bc1p` | `bc1` | `1` 或 `3` | +| **用途** | 支持 SegWit v1+ (如 Taproot) | 支持 SegWit v0 (P2WPKH 和 P2WSH) | 非 SegWit 和 SegWit P2SH | +| **校验和算法** | 修改的贝奇校验和算法 | 贝奇校验和算法 | Base58Check | +| **可读性和可扫描性** | 同 Bech32 | 更优的可读性和较低的打字错误率 | 较低的可读性和较高的打字错误率 | +| **推广程度** | Taproot更新后推广 | SegWit 地址的标准格式 | 比特币早期广泛使用 | + +### 格式和使用 + +- **格式**:`Bech32m` 地址的格式与 `Bech32` 相似,地址以 `bc1` 开头,随后是一个版本指示符,然后是编码的数据。 +- **使用**:自从 Bitcoin Core 0.21.1 版本开始支持 `Bech32m` 格式后,它被用于 Taproot 地址(即以 `bc1p` 开头的地址)。 + +## 编解码 + +在 JavaScript 中,使用 `bech32` 库来处理 `Bech32m` 的编码和解码同样非常直接。首先,你需要安装 `bech32` 库,它支持在 Node.js 环境中执行 `Bech32` 和 `Bech32m` 相关操作。 + +### 安装 `bech32` 库 + +在你的 Node.js 项目中,你可以通过 npm 或 yarn 来安装这个库: + +```bash +npm install bech32 +# 或者 +yarn add bech32 +``` + +### 编码示例 + +下面是如何将一组数据编码为 `Bech32m` 格式的示例: + +```javascript +const bech32 = require('bech32'); + +function encodeBech32m(data, hrp = 'bc') { + // 将数据从8位字节数组转换为5位数组 + const words = bech32.toWords(Buffer.from(data)); + + // 使用 bech32m 规范进行编码 + return bech32.encode(hrp, words, bech32.encodings.BECH32M); +} + +// 示例数据 +const data = [0x01, 0x02, 0x03, 0x04, 0x05]; + +// 编码 +const encoded = encodeBech32m(data); +console.log('Encoded Bech32m:', encoded); +``` + +### 解码示例 + +接下来是如何解码 `Bech32m` 格式的数据: + +```javascript +function decodeBech32m(bech32mStr) { + const decoded = bech32.decode(bech32mStr, bech32.encodings.BECH32M); + + // 将5位数组转换回8位字节 + const bytes = Buffer.from(bech32.fromWords(decoded.words)); + + return { + hrp: decoded.prefix, + data: bytes, + }; +} + +// 使用上面的编码结果进行解码 +const decoded = decodeBech32m(encoded); +console.log('Decoded data:', decoded.data); +``` + +## 编解码步骤 + +为了更深入理解 `Bech32m` 的编解码过程,让我们详细探讨其背后的数学和逻辑步骤。这种编码方式旨在提供更强的错误检测能力,并针对地址等用途进行优化。 + +### 核心组件和步骤 + +`Bech32m` 编解码涉及以下核心组件: + +1. **人类可读部分 (HRP)**:明确标识相关数据或网络的简短字符序列。 +2. **数据部分**:包括实际有效载荷和错误检测代码。 +3. **校验和算法**:生成和验证整个字符串的完整性。 + +### 编码过程 + +#### 1. 数据准备 + +将需要编码的数据(如公钥哈希)转换成一系列5位的数值,这是因为 `Bech32m` 的字符集能够表示从0到31的值,相比于完整的8位二进制表示,这种方式允许数据更有效地匹配到字符集。 + +**转换方法**:使用转换函数(如 `toWords`),将每个字节按位重新组合,从8位分组转换成5位分组。 + +#### 2. 计算校验和 + +`Bech32m` 使用一种多项式校验和算法,以确保传输的数据在传播过程中没有发生错误。这一算法基于BCH码,具体步骤如下: + +- **多项式定义**:使用特定的生成多项式进行计算,该多项式在设计时就考虑了捕获常见传输错误的需求。 +- **校验和的生成**:将HRP和数据部分一起,通过多项式算法生成一个32位的校验和。 + +#### 3. 结合数据 + +将转换后的数据与校验和结合,准备进行字符映射。 + +#### 4. 字符映射 + +使用 `Bech32m` 指定的字符集将数值转换为对应的字符,生成最终的编码字符串。字符集是一组32个字符,每个字符对应一个从0到31的值。 + +### 解码过程 + +解码是编码的逆过程,主要包括以下步骤: + +#### 1. 分离和验证 + +将输入的 `Bech32m` 字符串分解为HRP、数据部分和校验和。验证校验和以确认数据在传输过程中未被篡改或错误输入。 + +#### 2. 字符到数值的映射 + +将接收到的字符序列映射回相应的5位数值序列。 + +#### 3. 数据恢复 + +使用相同的位转换逻辑(反向操作 `fromWords`),将5位数值序列还原为原始的8位字节序列。 + +#### 4. 最终输出 + +提取数据,剔除校验和,返回原始数据和HRP。 + +## 拆解实现 + +### 1. 基础组件 + +首先,我们需要实现一些基础函数,包括多项式校验和计算和数据转换。 + +#### 多项式校验和 + +`Bech32m` 使用的多项式为 `x^5 + x^4 + x^3 + x^2 + 1`,我们需要一个函数来计算校验和: + +```javascript +const CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; + +// 将字符转换为数值 +function charToValue(char) { + const index = CHARSET.indexOf(char); + if (index === -1) { + throw new Error('Invalid character'); + } + return index; +} + +// 将数值转换为字符 +function valueToChar(value) { + return CHARSET[value]; +} + +// 多项式模数计算 Bech32 校验和 +function polymod(values) { + let checksum = 1; + for (let value of values) { + const top = checksum >> 25; + checksum = ((checksum & 0x1ffffff) << 5) ^ value; + for (let i = 0; i < 5; i++) { + if ((top >> i) & 1) { + checksum ^= (0x3b6a57b2 >> (5 * i)) & 0x1ffffff; + } + } + } + return checksum; +} + +// 扩展人类可读部分(HRP, Human-Readable Part)以帮助计算校验和 +function hrpExpand(hrp) { + const ret = []; + // 先将每个字符转换为其ASCII码除以32的余数 + for (let i = 0; i < hrp.length; i++) { + ret.push(hrp.charCodeAt(i) >> 5); + } + // 添加分隔符 + ret.push(0); + // 再将每个字符转换为其ASCII码的低5位 + for (let i = 0; i < hrp.length; i++) { + ret.push(hrp.charCodeAt(i) & 31); + } + return ret; +} + +// 计算校验和的最终值(Bech32m) +function createChecksum(hrp, data) { + const values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]); + const polymodValue = polymod(values) ^ 0x2bc830a3; // Bech32m 标识多项式 + let result = []; + for (let i = 0; i < 6; ++i) { + result.push((polymodValue >> (5 * (5 - i))) & 31); + } + return result; +} + +// 验证校验和 +function verifyChecksum(hrp, data) { + const expandedHrp = hrpExpand(hrp); + const values = expandedHrp.concat(data); + return polymod(values) === 0x2bc830a3; // 对于 Bech32, 有效校验和会返回1, 对于 Bech32m 需要验证不同的常量 +} +``` + +#### 数据转换 + +使用5比特群(bits)的数据表示方式进行转换: + +```javascript +function convertBits(data, fromBits, toBits, pad = true) { + let acc = 0; + let bits = 0; + const ret = []; + const maxv = (1 << toBits) - 1; + for (let i = 0; i < data.length; ++i) { + let value = data[i]; + if (value < 0 || value >> fromBits !== 0) { + return null; + } + acc = (acc << fromBits) | value; + bits += fromBits; + while (bits >= toBits) { + bits -= toBits; + ret.push((acc >> bits) & maxv); + } + } + if (pad) { + if (bits > 0) { + ret.push((acc << (toBits - bits)) & maxv); + } + } else if (bits >= fromBits || (acc << (toBits - bits)) & maxv) { + return null; + } + return ret; +} +``` + +### 2. 编码和解码实现 + +接下来,使用上述基础组件实现编码和解码函数: + +#### 编码函数 + +```javascript +function encodeBech32m(hrp, data) { + const combined = data.concat(createChecksum(hrp, data)); + let result = hrp + '1'; + for (let datum of combined) { + result += valueToChar(datum); + } + return result; +} +``` + +#### 解码函数 + +```javascript +function decodeBech32m(bech32mStr) { + const pos = bech32mStr.lastIndexOf('1'); + const hrp = bech32mStr.slice(0, pos); + const data = []; + for (let i = pos + 1; i < bech32mStr.length; ++i) { + data.push(charToValue(bech32mStr[i])); + } + if (!verifyChecksum(hrp, data)) { + throw new Error('Invalid checksum'); + } + return { hrp, data: data.slice(0, data.length - 6) }; +} +``` + +### 3. 示例使用 + +```javascript +const hrp = 'bc'; +const data = [15, 2, 20, 3, 5, 18]; // 随机示例数据 +const encoded = encodeBech32m(hrp, data); +console.log('Encoded:', encoded); + +const decoded = decodeBech32m(encoded); +console.log('Decoded:', decoded); +``` + +这种方式将手动实现 `Bech32m` 编解码过程,展示了如何从基本原理开始构建此功能。 + +## 在BTC中的应用 + +#### SegWit 地址支持 + +- **SegWit v0 地址**:使用 `Bech32` 编码,这些地址以 `bc1q` 开头。 +- **SegWit v1+ 地址(包括 Taproot)**:使用 `Bech32m` 编码,这些地址以 `bc1p` 开头。 + +#### 优点 + +1. **更低的交易费用**:SegWit 地址允许比特币交易以更低的费用进行,因为它们更有效地利用区块空间。 +2. **增强的错误检测能力**:`Bech32m` 改进了校验和算法,提供了更强的错误检测能力,减少了地址输入错误的风险。 +3. **更好的用户体验**:地址全小写,易于手工抄写和阅读,减少了错误输入的机会。 + +#### Taproot 与 `Bech32m` + +Taproot 是比特币的一个重要升级,它引入了 Schnorr 签名和 Merklized Abstract Syntax Trees (MAST),增强了隐私、扩展性和效率。`Bech32m` 是支持 Taproot 地址的推荐编码方式,因为 Taproot 主要使用 SegWit v1。这种新的地址格式使得利用 Taproot 所提供的所有优势变得更加高效和安全。 + +`Bech32m` 格式的引入是比特币协议持续进化的一部分,它不仅提高了地址的错误校验能力,还通过支持如 Taproot 这样的先进特性,推动了整个网络向前发展。对于用户和开发者而言,了解并适应这种新的地址格式是非常重要的,它将帮助他们最大化比特币的潜力。 diff --git a/BTC/Basic/tools/dust.md b/BTC/Basic/tools/dust.md new file mode 100644 index 000000000..b9cda55ea --- /dev/null +++ b/BTC/Basic/tools/dust.md @@ -0,0 +1,216 @@ +在比特币网络中,“dust” 或 “尘埃交易”指的是非常小的比特币金额,其大小甚至不足以支付发送这笔金额所需的交易费。因为每笔比特币交易都需要支付矿工费以保证交易被网络确认,如果交易中的金额太小,不足以覆盖这笔费用,那么这笔金额实际上就变得无法使用。 + +# Dust 定义 + +参考bitcoin中的[定义](https://github.com/bitcoin/bitcoin/blob/160d23677ad799cf9b493eaa923b2ac080c3fb8e/src/policy/policy.cpp#L26-L63) + +> "Dust" is defined in terms of dustRelayFee, +> which has units satoshis-per-kilobyte. +> If you'd pay more in fees than the value of the output +> to spend something, then we consider it dust. +> A typical spendable non-segwit txout is 34 bytes big, and will +> need a CTxIn of at least 148 bytes to spend: +> so dust is a spendable txout less than +> 182*dustRelayFee/1000 (in satoshis). +> 546 satoshis at the default rate of 3000 sat/kvB. +> A typical spendable segwit P2WPKH txout is 31 bytes big, and will +> need a CTxIn of at least 67 bytes to spend: +> so dust is a spendable txout less than +> 98*dustRelayFee/1000 (in satoshis). +> 294 satoshis at the default rate of 3000 sat/kvB + +# 交易结构 + +![transaction](./images/transaction.png) + +# 常用地址的计算 + +## P2PKH - 546 + +### Output + +```js +Size = Amout + ScriptPubKey Size + ScriptPubKey = 8 + 1 + 25 = 34 +``` + +#### ScriptPubKey + +```js +// 模版 共25字节 +OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG + +OP_DUP // 复制栈顶的元素 +OP_HASH160 // 弹出栈顶元素,计算其SHA-256散列,然后计算RIPEMD-160散列 +OP_PUSHBYTES_20 // 推送20字节到栈顶 +18e1fad25b2983d5dbb2e2b96e3ce756a69b3bc2 // 公钥哈希 +OP_EQUALVERIFY // 比较栈顶元素 +OP_CHECKSIG // 验证数字签名 +``` + +### Input + +```js +Size = TXID + VOUT + ScriptSig Size + ScriptSig + Sequence = 32 + 4 + 1 + 107 + 4 = 148 +``` + +#### ScriptSig + +```js +// 模版 共107字节 + + +OP_PUSHBYTES_72 // 推送72字节到栈顶 +3045022100c233c3a8a510e03ad18b0a24694ef00c78101bfd5ac075b8c1037952ce26e91e02205aa5f8f88f29bb4ad5808ebc12abfd26bd791256f367b04c6d955f01f28a772401 // 签名数据 +OP_PUSHBYTES_33 // 推送33字节到栈顶 +03480b6822120e9936b43859d84c380583c3d0292409b21453ae962815090f8117 // 压缩公钥 +``` + +### DUST + +```js +dust = (input + output) * feeRate = (34 + 148) * 3 = 546 +``` + +## P2SH-P2PKH - 540 1%⬇️ + +### Output + +```js +Size = Amout + ScriptPubKey Size + ScriptPubKey = 8 + 1 + 23 = 32 +``` + +#### ScriptPubKey + +```js +// 模版 共23字节 +OP_HASH160 OP_EQUAL +其中 +scriptHash = hash160(OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG) + +OP_HASH160 // 弹出栈顶元素,计算其SHA-256散列,然后计算RIPEMD-160散列 +OP_PUSHBYTES_20 // 推送20字节到栈顶 +18e1fad25b2983d5dbb2e2b96e3ce756a69b3bc2 // 脚本哈希 +OP_EQUAL // 比较栈顶两个元素相等 +``` + +### Input + +```js +Size = TXID + VOUT + ScriptSig Size + ScriptSig + Sequenc = 32 + 4 + 1 + 107 + 4 = 148 +``` + +#### ScriptSig + +```js +// 模版 共107字节 + + +OP_PUSHBYTES_72 // 推送72字节到栈顶 +3045022100c233c3a8a510e03ad18b0a24694ef00c78101bfd5ac075b8c1037952ce26e91e02205aa5f8f88f29bb4ad5808ebc12abfd26bd791256f367b04c6d955f01f28a772401 // 签名数据 +OP_PUSHBYTES_33 // 推送33字节到栈顶 +03480b6822120e9936b43859d84c380583c3d0292409b21453ae962815090f8117 // 压缩公钥 +``` + +### DUST + +```js +dust = (input + output) * feeRate = (32 + 148) * 3 = 540 +``` + +## P2WPKH - 294 46%⬇️ + +### Output + +```js +Size = Amout + ScriptPubKey Size + ScriptPubKey = 8 + 1 + 22 = 31 +``` + +#### ScriptPubKey + +```js +// 模版 共22字节 +OP_0 + +OP_0 // 表示版本号,对于P2WPKH是0 +OP_PUSHBYTES_20 // 推送20字节到栈顶 +18e1fad25b2983d5dbb2e2b96e3ce756a69b3bc2 // 公钥哈希 +``` + +### Input + +```js +Size = TXID + VOUT + Sequence + Witness/4 = 32 + 4 + 4 + 108/4 = 67 +``` + +#### ScriptSig + +``` +// 模版 共107字节 + + +02 // 见证数据的数量 +48 // 第一个见证数据的长度72字节 +3045022100a5eec95c65d2dd679e8fdcc10668d5d51446f24d1557859cb543e570201257c6022011df04f803a08c97de213ddf211ad193045ff25e59857324a28656de1961351c01 // 签名数据 +21 // 第二个见证数据的长度33字节 +0295f97f41d0d523bddf39e77e16cc0dda2e56b9bd9fdea10133656635b2c28a39 // 压缩公钥 +``` + +### DUST + +```js +dust = (input + output) * feeRate = (31 + 67) * 3 = 294 +``` + +## P2TR(key path) - 300 26.7%⬇️ + +### Output + +```js +Size = Amout + ScriptPubKey Size + ScriptPubKey = 8 + 1 + 34 = 43 +``` + +#### ScriptPubKey + +```js +// 模版 共22字节 +OP_1 + +OP_1 // 表示版本号,对于P2TR是1 +OP_PUSHBYTES_32 // 推送32字节到栈顶 +2c3ee7cdb92394e32e82ec5bc8860c8888df6a9910537e90c75079726a2a8469 // tweaked-public-key +``` + +### Input + +```js +Size = TXID + VOUT + Sequence + Witness/4 = 32 + 4 + 4 + 67/4 = 57 +``` + +#### ScriptSig + +```js +// 模版 共67字节 +[signature] [script] [control block] + +01 // Witness 元素的个数 +40 // schnorr签名长度 +743bbb3df4e95df5e70c2a3e72dd9d05933a018ad1bbd4b700841824ed18bebbc54d1b4d03b252aaa5c373390c68ba119fdf70e0bab71694a4db0da3fe8fefcc // schnorr签名 +00 // 签名类型 +``` + +### DUST + +```js +dust = (input + output) * feeRate = (43 + 57) * 3 = 300 +``` + +# 主流库的实现 + +## Bitcoin + +已经添加dust的计算[GetDustThreshold](https://github.com/bitcoin/bitcoin/blob/160d23677ad799cf9b493eaa923b2ac080c3fb8e/src/policy/policy.cpp#L26-L63) +同时在[IsStandardTx](https://github.com/bitcoin/bitcoin/blob/160d23677ad799cf9b493eaa923b2ac080c3fb8e/src/policy/policy.cpp#L70-L92)中会把包含小于dust的输出交易置为异常交易 + +## Bitcoinjs + +Bitcoinjs 将会在7.0.0中添加[dustAmountFromOutputScript](https://github.com/bitcoinjs/bitcoinjs-lib/pull/2010)的计算,不过计算逻辑只会通过是否是Witness返回546或294 diff --git a/BTC/Basic/tools/images/transaction.png b/BTC/Basic/tools/images/transaction.png new file mode 100644 index 000000000..f1f8d4a74 Binary files /dev/null and b/BTC/Basic/tools/images/transaction.png differ diff --git a/BTC/Basic/wallet/README.md b/BTC/Basic/wallet/README.md new file mode 100644 index 000000000..9a3ccb82d --- /dev/null +++ b/BTC/Basic/wallet/README.md @@ -0,0 +1,38 @@ +# 比特币地址生成脚本 +这个项目包含一个常见的 JavaScript 脚本,用于生成不同类型的比特币地址,包括 Legacy、Nested Segwit、Native Segwit 和 Taproot 地址。它使用 bitcoinjs-lib 库和 ecpair 以及 tiny-secp256k1 模块来创建地址。 + +# 功能 +生成四种类型的比特币地址: +- Legacy (P2PKH) +- Nested Segwit (P2SH-P2WPKH) +- Native Segwit (P2WPKH) +- Taproot (P2TR) + +# 环境设置 +安装 Node.js: 确保您的系统上安装了 Node.js。可以在 Node.js 官网下载并安装。 + +安装依赖: 项目依赖于 bitcoinjs-lib、ecpair 和 tiny-secp256k1。可以通过运行以下命令来安装这些依赖项: + +```js +npm install bitcoinjs-lib ecpair tiny-secp256k1 +``` +# 运行脚本 +在安装所有依赖后,您可以运行脚本来生成比特币地址。使用以下命令来运行脚本: + +```js +node <脚本文件名>.js +``` +替换 <脚本文件名> 为您的 JavaScript 文件名。 + +# 输出 +脚本将在控制台中输出以下格式的地址: + +```js +Legacy Address: 18RKdzEVDe6oCxrdeiipZGtKYWxTXmaxh3 +Nested Segwit Address: 385Q6vxTS5My1rZ7vs6jSonuQDB987o2p2 +Native Segwit Address: bc1qxwy6vyfedw67sdr754avqcgrnw9sfpzuqgde4d +Taproot Address: bc1pvc3cpr6c2eejzylks2cg9vhanqepk3hmd0ssn8etzmf2r7y4q40q77ljsl +``` + +生成的地址仅用于演示和教育目的。在生产环境中使用时,请确保理解相关的安全风险。 +Taproot 地址的支持可能取决于 bitcoinjs-lib 的版本。 diff --git a/BTC/Basic/wallet/address.js b/BTC/Basic/wallet/address.js new file mode 100644 index 000000000..284f5e437 --- /dev/null +++ b/BTC/Basic/wallet/address.js @@ -0,0 +1,45 @@ +import ecpair from 'ecpair'; +import * as tinysecp from 'tiny-secp256k1'; +import bitcoin from 'bitcoinjs-lib'; + +const { ECPairFactory } = ecpair; +const ECPair = ECPairFactory(tinysecp); +bitcoin.initEccLib(tinysecp); + +function toXOnly(pubKey) { + return pubKey.length === 32 ? pubKey : pubKey.slice(1, 33); +} + +function generateAddress(networkType, addressType) { + const network = bitcoin.networks[networkType]; + // 生成Bitcoin私钥和地址 + const keyPair = ECPair.makeRandom({ network }); + + let address; + switch (addressType) { + case 'legacy': + address = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network }).address; + break; + case 'nestedSegwit': + address = bitcoin.payments.p2sh({ + redeem: bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network }), + network, + }).address; + break; + case 'nativeSegwit': + address = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network }).address; + break; + case 'taproot': + const pubkey = toXOnly(keyPair.publicKey); + address = bitcoin.payments.p2tr({ pubkey, network }).address; + break; + default: + address = 'Invalid address type'; + } + return address; +} + +console.log('Legacy Address:', generateAddress('bitcoin', 'legacy')); +console.log('Nested Segwit Address:', generateAddress('bitcoin', 'nestedSegwit')); +console.log('Native Segwit Address:', generateAddress('bitcoin', 'nativeSegwit')); +console.log('Taproot Address:', generateAddress('bitcoin', 'taproot')); diff --git a/BTC/Basic/wallet/package.json b/BTC/Basic/wallet/package.json new file mode 100644 index 000000000..bf4ec8502 --- /dev/null +++ b/BTC/Basic/wallet/package.json @@ -0,0 +1,17 @@ +{ + "name": "btc-wallet", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "bitcoinjs-lib": "^6.1.5", + "ecpair": "^2.1.0", + "tiny-secp256k1": "^2.2.3" + } +} diff --git a/BTC/README-ES.md b/BTC/README-ES.md new file mode 100644 index 000000000..ba7ab380c --- /dev/null +++ b/BTC/README-ES.md @@ -0,0 +1,145 @@ +# Bitcoin (BTC) + +This repository aims to provide comprehensive information on Bitcoin (BTC) technology, covering its basic functions, advanced applications, and various protocols and tools in its ecosystem. Whether you are a beginner or an experienced developer, this guide will help you gain a deeper understanding and make efficient use of Bitcoin technology. + +## Introduction + +Bitcoin is the first decentralized digital currency based on blockchain technology, proposed by an individual or group using the pseudonym "Satoshi Nakamoto" in 2008 and officially launched in 2009. It disrupts traditional financial systems by enabling trustless value transfers through cryptography and a distributed network. It functions as a payment tool, an investment asset, and a social experiment that redefines the boundaries of money and trust. + +Bitcoin combines cryptography, economics, and distributed systems to pioneer decentralized digital assets. Its value stems from technical reliability (censorship resistance, scarcity), social consensus (the "digital gold" narrative), and macroeconomic conditions (fiat currency over-issuance, geopolitical conflicts). Despite challenges such as scalability bottlenecks, energy debates, and regulatory uncertainty, Bitcoin continues to drive financial innovation, becoming the core symbol of the blockchain revolution and redefining the future of money, trust, and value. + + +##Core Technical Mechanisms + +-Blockchain and Decentralization +Transaction data is stored on a transparent, distributed ledger (blockchain) maintained by a global network of nodes, with no central authority. A chain structure (blocks linked via hash values) and timestamping ensure data immutability. + +-Cryptography and Security +Uses the SHA-256 hashing algorithm and asymmetric encryption (public addresses and private key signatures) to secure transaction ownership and integrity. Proof of Work (PoW): Miners compete computationally to validate transactions and create new blocks, consuming energy to safeguard the network while earning newly minted coins as rewards. + +-Deflationary Model +Total supply capped at 21 million coins, with issuance halved every four years ("halving"), ending in 2140. Its scarcity mirrors that of gold. + + +##Key Features and Innovations + +-Trustless System +Operates without reliance on banks or governments, governed solely by code. Users fully control assets (ownership via private keys). + +-Censorship Resistance and Global Accessibility +Borderless transactions on a resilient network, ideal for cross-border payments and inflation hedging (e.g., Bitcoin’s adoption as legal tender in El Salvador). + +-Pseudonymity +Transactions use encrypted addresses, not tied to real-world identities, but on-chain records are publicly traceable. Privacy tools (e.g., coin mixers) enhance anonymity. + +-Irreversibility +Confirmed transactions cannot be reversed, reducing fraud but requiring caution (e.g., accidental transfers are permanent). + +-High Volatility +Prices fluctuate due to market demand, regulation, and speculation. Bitcoin peaked at 60,000 in 2021, then fell to16,000 in 2022. + + +##Historical Evolution and Societal Impact + +-Milestones + +2008 Whitepaper: Satoshi Nakamoto published Bitcoin: A Peer-to-Peer Electronic Cash System, outlining a decentralized currency vision. + +2010 First Real-World Transaction: A programmer paid 10,000 BTC for two pizzas (worth billions today), marking Bitcoin’s practical adoption. + +2017 Fork and Scaling Debate: Community disputes split Bitcoin into BTC (main chain) and BCH (Bitcoin Cash), exposing governance challenges in decentralized systems. + +2021 Institutional Adoption: Companies like Tesla and MicroStrategy added Bitcoin to their balance sheets, and Bitcoin futures ETFs gained approval, boosting mainstream acceptance. + +-Expanding Use Cases + +Payment Network: The Lightning Network (Layer 2) enables fast microtransactions, addressing the main chain’s low throughput (~7 transactions/second). + +Store of Value: Used in hyperinflationary economies (e.g., Argentina, Nigeria) to hedge against fiat currency collapse. + +Ecosystem Innovation: Spurred advancements like smart contracts, DeFi (decentralized finance), and NFTs, though Bitcoin remains focused on its "digital gold" role. + +This knowledge repository will guide you through Bitcoin's basic functionalities, advanced features, and ecosystem applications, helping you stay at the forefront of this rapidly evolving field. + +## Task Status Legend + +⬜ Not Started ⌛ In Progress ✅ Completed + +## 1. Basic Features + +### 1.1 Payment Addresses + +Types of Bitcoin addresses and their encoding: + +- **P2PKH** (Pay to Public Key Hash) ✅ +- **P2SH-P2PKH** (Pay to Script Hash - Pay to Public Key Hash) ✅ +- **P2WPKH** (Pay to Witness Public Key Hash) ✅ +- **P2TR** (Pay to Taproot) ✅ +- **Base58 Encoding** ✅ +- **Bech32 Encoding** ✅ +- **Bech32m Encoding** ✅ + +### 1.2 Wallets + +Types of Bitcoin wallets and their characteristics: + +- **Standard Wallets** ⌛ +- **HD Wallets (Hierarchical Deterministic Wallets)** ✅ + +### 1.3 Transactions + +Key knowledge about Bitcoin transactions: + +- **UTXO Analysis** ⬜ +- **Coinbase Transactions** ⬜ +- **Transaction Construction** ⬜ +- **Fee Estimation** ⬜ +- **Signature Algorithms** ✅ +- **Transaction Acceleration** ⬜ + +### 1.4 Tools + +Common tools in the Bitcoin ecosystem: + +- **Blockchain Explorers** ⌛ +- **Network Hash Rate Tools** ⬜ +- **API Services** ⬜ +- **Data Analysis Tools** ⬜ + +## 2. Advanced Features + +### 2.1 PSBT (Partially Signed Bitcoin Transaction) + +Introduction and usage of PSBT: + +- **PSBT Protocol Introduction** ✅ +- **Creating and Parsing PSBT** ✅ +- **PSBT V2** ⬜ + +### 2.2 Taproot + +Introduction to Taproot and its benefits: + +- **Schnorr Signatures** ✅ +- **MAST** (Merkelized Abstract Syntax Tree) ✅ +- **Privacy and Efficiency Improvements** ⬜ +- **Key Path vs. Script Path Differences** ⬜ + +### 2.3 Multisig Addresses + +Usage and advantages of multisig addresses: + +- **Multisig Address Introduction** ✅ +- **OP_CHECKMULTISIG** ✅ +- **OP_CHECKMULTISIGVERIFY** ✅ +- **OP_CHECKSIGADD** ✅ + +## 3. Ecosystem Applications + +- **BRC20 Protocol Overview** ✅ +- **ARC20 Protocol Overview** ✅ +- **Runes Protocol Overview** ✅ +- **Lightning Network Introduction** ⌛ +- **Core Logic of the Lightning Network** ⌛ +- **OP_CAT** ✅ +- **Babylon | BTC Staking** ⬜ diff --git a/BTC/README.md b/BTC/README.md new file mode 100644 index 000000000..475079afa --- /dev/null +++ b/BTC/README.md @@ -0,0 +1,127 @@ +# 比特币 (BTC) + +本知识库旨在提供有关比特币(BTC)技术的全面信息,涵盖基本功能、进阶应用以及生态系统中的各类协议和工具。无论您是初学者还是有经验的开发者,本指南都将帮助您更好地理解和使用比特币技术。 + +## 简介 + +比特币是首个基于区块链技术的去中心化数字货币,由化名“中本聪”的个人或团队于2008年提出,2009年正式运行。它颠覆传统金融体系,通过密码学与分布式网络实现无需信任第三方的价值转移,既是支付工具,也是投资标的,更被视为一场社会实验,重塑货币与信任的边界。 + +比特币融合密码学、经济学与分布式系统,开创了去中心化数字资产的先河。其价值源于技术可靠性(抗审查、稀缺性)、社会共识(“数字黄金”叙事)及宏观环境(法币超发、地缘冲突)。尽管面临扩容瓶颈、能源争议与监管不确定性,比特币仍持续推动金融体系变革,成为区块链革命的核心象征,重新定义货币、信任与价值的未来形态。 + + +##核心技术机制 + +-区块链与去中心化 +交易数据存储在公开透明的分布式账本(区块链)中,由全球节点共同维护,无中央机构控制。链式结构(区块通过哈希值串联)与时间戳技术确保数据不可篡改。 + +-密码学与安全 +使用SHA-256哈希算法和非对称加密(公钥地址与私钥签名),保障交易所有权和安全性。工作量证明(PoW):矿工通过算力竞争验证交易并生成新区块,消耗能源换取网络安全,同时新币作为奖励发行。 + +-通缩模型 +总量上限2100万枚,每四年“减半”新币发行量,2140年全部挖完,稀缺性类比黄金。 + +##核心特点与创新 + +-去信任化 +无需银行或政府背书,代码规则驱动系统运行,用户自主掌控资产(私钥即所有权)。 + +-抗审查与全球化 +交易不受国界限制,节点网络抗屏蔽,尤其适用于跨境支付与通胀国家避险(如萨尔瓦多定为法币)。 + +-伪匿名性 +用户通过加密地址交易,不直接绑定身份,但链上记录公开可追溯,需借助混币器等工具增强隐私。 + +-不可逆性 +交易一经区块链确认无法撤销,降低欺诈风险,但也要求用户谨慎操作(如误转账无法追回)。 + +-高波动性 +市场供需、政策监管与投机情绪导致价格剧烈波动,2021年曾突破6万美元,2022年跌至1.6万美元。 + +##历史演进与社会影响 + +-里程碑事件 + +2008年白皮书:中本聪发布《比特币:一种点对点的电子现金系统》,提出去中心化货币愿景。 + +2010年首笔实物交易:程序员用1万BTC购买2个披萨(现价值数亿美元),开启加密货币实用化。 + +2017年分叉与扩容争议:社区分歧导致比特币分叉为BTC(主链)与BCH(比特币现金),暴露去中心化治理难题。 + +2021年机构化:特斯拉、MicroStrategy等公司纳入资产负债表,比特币期货ETF获批,推动主流金融接纳。 + +-应用场景扩展 + +支付网络:闪电网络(Layer 2)实现小额快速交易,解决主链吞吐量低(约7笔/秒)问题。 + +价值存储:部分国家民众(如阿根廷、尼日利亚)将其作为抗通胀资产,对冲法币贬值风险。 + +技术衍生生态:催生智能合约、DeFi(去中心化金融)、NFT等创新,但比特币自身仍聚焦“数字黄金”定位。 +通过本知识库,您将学习比特币的核心功能、进阶特性以及生态系统应用,助您在这一快速发展的领域中保持领先。 + +## 任务状态图例: +⬜ 未开始 ⌛ 进行中 ✅ 已完成 + +## 1. 基本功能 + +### 1.1 支付地址 +比特币地址类型及编码方式: +- **P2PKH** (支付到公钥哈希) ✅ +- **P2SH-P2PKH** (支付到脚本哈希 - 支付到公钥哈希) ✅ +- **P2WPKH** (支付到见证公钥哈希) ✅ +- **P2TR** (支付到 Taproot) ✅ +- **Base58 编码** ✅ +- **Bech32 编码** ✅ +- **Bech32m 编码** ✅ + +### 1.2 钱包 +比特币钱包类型及其特性: +- **标准钱包** ⌛ +- **分层确定性钱包(HD 钱包)** ✅ + +### 1.3 交易 +比特币交易的相关知识: +- **UTXO解析** ⌛ +- **Coinbase交易** ⬜ +- **交易构建** ⌛ +- **费用估算** ⌛ +- **签名算法** ✅ + + +### 1.4 工具 +比特币生态系统中的常用工具: +- **区块链浏览器** ⌛ +- **全网算力** ⬜ +- **API 服务** ⬜ +- **数据分析工具** ⬜ + +## 2. 进阶功能 + +### 2.1 PSBT (部分签名比特币交易) +PSBT 的介绍和使用: +- **PSBT 协议简介** ✅ +- **创建和解析 PSBT** ✅ +- **PSBT V2** ⬜ + +### 2.2 Taproot +Taproot 技术的介绍及其优势: +- **施诺尔签名** ✅ +- **MAST** (Merkelized 抽象语法树) ✅ +- **隐私和效率改进** ⬜ +- **Key Path和Script Path的区别** ⬜ + +### 2.3 多重签名地址 +多重签名地址的使用及优势: +- **多重签名简介** ✅ +- **OP_CHECKMULTISIG** ✅ +- **OP_CHECKMULTISIGVERIFY** ✅ +- **OP_CHECKSIGADD** ✅ + +## 3. 生态系统应用 +- **BRC20 协议介绍** ✅ +- **ARC20 协议介绍** ✅ +- **Runes 协议介绍** ✅ +- **闪电网络简介** ⌛ +- **闪电网络核心逻辑** ⌛ +- **OP_CAT** ✅ +- **Babylon | BTC Staking** ⬜ + diff --git a/basic/85-REVM/.env.example b/basic/85-REVM/.env.example new file mode 100644 index 000000000..1953cc8ea --- /dev/null +++ b/basic/85-REVM/.env.example @@ -0,0 +1,2 @@ +HTTPS_URL= +WSS_URL= \ No newline at end of file diff --git a/basic/85-REVM/.gitignore b/basic/85-REVM/.gitignore new file mode 100644 index 000000000..ea8c4bf7f --- /dev/null +++ b/basic/85-REVM/.gitignore @@ -0,0 +1 @@ +/target diff --git a/basic/85-REVM/Cargo.lock b/basic/85-REVM/Cargo.lock new file mode 100644 index 000000000..0121e4c89 --- /dev/null +++ b/basic/85-REVM/Cargo.lock @@ -0,0 +1,5027 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-eip2930" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "k256", + "serde", +] + +[[package]] +name = "alloy-primitives" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c77490fe91a0ce933a1f219029521f20fc28c2c0ca95d53fa4da9c00b8d9d4e" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 2.0.1", + "foldhash", + "hashbrown", + "indexmap", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.8.5", + "ruint", + "rustc-hash", + "serde", + "sha3 0.10.8", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version 0.4.1", +] + +[[package]] +name = "aurora-engine-modexp" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518bc5745a6264b5fd7b09dffb9667e400ee9e2bbe18555fac75e1fe9afa0df9" +dependencies = [ + "hex", + "num", +] + +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty 1.1.0", + "radium 0.6.2", + "tap", + "wyz 0.2.0", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty 2.0.0", + "radium 0.7.0", + "tap", + "wyz 0.5.1", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "blst" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fd49896f12ac9b6dcd7a5998466b9b58263a695a3dd1ecc1aaca2e12a90b080" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "sha2", + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" + +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "serde", +] + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "c-kzg" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0307f72feab3300336fb803a57134159f6e20139af1357f36c54cb90d8e8928" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", + "serde", +] + +[[package]] +name = "camino" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.26", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "cc" +version = "1.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "coins-bip32" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" +dependencies = [ + "bs58", + "coins-core", + "digest 0.10.7", + "hmac", + "k256", + "serde", + "sha2", + "thiserror 1.0.69", +] + +[[package]] +name = "coins-bip39" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" +dependencies = [ + "bitvec 1.0.1", + "coins-bip32", + "hmac", + "once_cell", + "pbkdf2 0.12.2", + "rand 0.8.5", + "sha2", + "thiserror 1.0.69", +] + +[[package]] +name = "coins-core" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" +dependencies = [ + "base64 0.21.7", + "bech32", + "bs58", + "digest 0.10.7", + "generic-array", + "hex", + "ripemd", + "serde", + "serde_derive", + "sha2", + "sha3 0.10.8", + "thiserror 1.0.69", +] + +[[package]] +name = "colored" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f741c91823341bebf717d4c71bda820630ce065443b58bd1b7451af008355" +dependencies = [ + "is-terminal", + "lazy_static", + "winapi", +] + +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + +[[package]] +name = "const-hex" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e22e0ed40b96a48d3db274f72fd365bd78f67af39b6bbd47e8a15e1c6207ff" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl 2.0.1", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "ena" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" +dependencies = [ + "log", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enr" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a3d8dc56e02f954cac8eb489772c552c473346fc34f67412bb6244fd647f7e4" +dependencies = [ + "base64 0.21.7", + "bytes", + "hex", + "k256", + "log", + "rand 0.8.5", + "rlp", + "serde", + "sha3 0.10.8", + "zeroize", +] + +[[package]] +name = "enumn" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "eth-encode-packed" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6594e2bd74c078c13ccce9f59bae7325e6a66e623c4f52648d967c59fff3afbd" +dependencies = [ + "ethabi 14.1.0", + "hex", +] + +[[package]] +name = "eth-keystore" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" +dependencies = [ + "aes", + "ctr", + "digest 0.10.7", + "hex", + "hmac", + "pbkdf2 0.11.0", + "rand 0.8.5", + "scrypt", + "serde", + "serde_json", + "sha2", + "sha3 0.10.8", + "thiserror 1.0.69", + "uuid", +] + +[[package]] +name = "ethabi" +version = "14.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01317735d563b3bad2d5f90d2e1799f414165408251abb762510f40e790e69a" +dependencies = [ + "anyhow", + "ethereum-types 0.11.0", + "hex", + "serde", + "serde_json", + "sha3 0.9.1", + "thiserror 1.0.69", + "uint", +] + +[[package]] +name = "ethabi" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" +dependencies = [ + "ethereum-types 0.14.1", + "hex", + "once_cell", + "regex", + "serde", + "serde_json", + "sha3 0.10.8", + "thiserror 1.0.69", + "uint", +] + +[[package]] +name = "ethbloom" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb684ac8fa8f6c5759f788862bb22ec6fe3cb392f6bfd08e3c64b603661e3f8" +dependencies = [ + "crunchy", + "fixed-hash 0.7.0", + "impl-rlp", + "impl-serde 0.3.2", + "tiny-keccak", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash 0.8.0", + "impl-codec 0.6.0", + "impl-rlp", + "impl-serde 0.4.0", + "scale-info", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64b5df66a228d85e4b17e5d6c6aa43b0310898ffe8a85988c4c032357aaabfd" +dependencies = [ + "ethbloom 0.11.1", + "fixed-hash 0.7.0", + "impl-rlp", + "impl-serde 0.3.2", + "primitive-types 0.9.1", + "uint", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom 0.13.0", + "fixed-hash 0.8.0", + "impl-codec 0.6.0", + "impl-rlp", + "impl-serde 0.4.0", + "primitive-types 0.12.2", + "scale-info", + "uint", +] + +[[package]] +name = "ethers" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816841ea989f0c69e459af1cf23a6b0033b19a55424a1ea3a30099becdb8dec0" +dependencies = [ + "ethers-addressbook", + "ethers-contract", + "ethers-core", + "ethers-etherscan", + "ethers-middleware", + "ethers-providers", + "ethers-signers", + "ethers-solc", +] + +[[package]] +name = "ethers-addressbook" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5495afd16b4faa556c3bba1f21b98b4983e53c1755022377051a975c3b021759" +dependencies = [ + "ethers-core", + "once_cell", + "serde", + "serde_json", +] + +[[package]] +name = "ethers-contract" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fceafa3578c836eeb874af87abacfb041f92b4da0a78a5edd042564b8ecdaaa" +dependencies = [ + "const-hex", + "ethers-contract-abigen", + "ethers-contract-derive", + "ethers-core", + "ethers-providers", + "futures-util", + "once_cell", + "pin-project", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "ethers-contract-abigen" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04ba01fbc2331a38c429eb95d4a570166781f14290ef9fdb144278a90b5a739b" +dependencies = [ + "Inflector", + "const-hex", + "dunce", + "ethers-core", + "ethers-etherscan", + "eyre", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "reqwest", + "serde", + "serde_json", + "syn 2.0.103", + "toml", + "walkdir", +] + +[[package]] +name = "ethers-contract-derive" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87689dcabc0051cde10caaade298f9e9093d65f6125c14575db3fd8c669a168f" +dependencies = [ + "Inflector", + "const-hex", + "ethers-contract-abigen", + "ethers-core", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.103", +] + +[[package]] +name = "ethers-core" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d80cc6ad30b14a48ab786523af33b37f28a8623fc06afd55324816ef18fb1f" +dependencies = [ + "arrayvec", + "bytes", + "cargo_metadata", + "chrono", + "const-hex", + "elliptic-curve", + "ethabi 18.0.0", + "generic-array", + "k256", + "num_enum", + "once_cell", + "open-fastrlp", + "rand 0.8.5", + "rlp", + "serde", + "serde_json", + "strum", + "syn 2.0.103", + "tempfile", + "thiserror 1.0.69", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "ethers-etherscan" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79e5973c26d4baf0ce55520bd732314328cabe53193286671b47144145b9649" +dependencies = [ + "chrono", + "ethers-core", + "reqwest", + "semver 1.0.26", + "serde", + "serde_json", + "thiserror 1.0.69", + "tracing", +] + +[[package]] +name = "ethers-middleware" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f9fdf09aec667c099909d91908d5eaf9be1bd0e2500ba4172c1d28bfaa43de" +dependencies = [ + "async-trait", + "auto_impl", + "ethers-contract", + "ethers-core", + "ethers-etherscan", + "ethers-providers", + "ethers-signers", + "futures-channel", + "futures-locks", + "futures-util", + "instant", + "reqwest", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tracing", + "tracing-futures", + "url", +] + +[[package]] +name = "ethers-providers" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6434c9a33891f1effc9c75472e12666db2fa5a0fec4b29af6221680a6fe83ab2" +dependencies = [ + "async-trait", + "auto_impl", + "base64 0.21.7", + "bytes", + "const-hex", + "enr", + "ethers-core", + "futures-channel", + "futures-core", + "futures-timer", + "futures-util", + "hashers", + "http", + "instant", + "jsonwebtoken", + "once_cell", + "pin-project", + "reqwest", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tokio-tungstenite", + "tracing", + "tracing-futures", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "ws_stream_wasm", +] + +[[package]] +name = "ethers-signers" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228875491c782ad851773b652dd8ecac62cda8571d3bc32a5853644dd26766c2" +dependencies = [ + "async-trait", + "coins-bip32", + "coins-bip39", + "const-hex", + "elliptic-curve", + "eth-keystore", + "ethers-core", + "rand 0.8.5", + "sha2", + "thiserror 1.0.69", + "tracing", +] + +[[package]] +name = "ethers-solc" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66244a771d9163282646dbeffe0e6eca4dda4146b6498644e678ac6089b11edd" +dependencies = [ + "cfg-if", + "const-hex", + "dirs", + "dunce", + "ethers-core", + "glob", + "home", + "md-5", + "num_cpus", + "once_cell", + "path-slash", + "rayon", + "regex", + "semver 1.0.26", + "serde", + "serde_json", + "solang-parser", + "svm-rs", + "thiserror 1.0.69", + "tiny-keccak", + "tokio", + "tracing", + "walkdir", + "yansi", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fern" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" +dependencies = [ + "colored 1.9.4", + "log", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-locks" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" +dependencies = [ + "futures-channel", + "futures-task", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" +dependencies = [ + "gloo-timers", + "send_wrapper 0.4.0", +] + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashers" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2bca93b15ea5a746f220e56587f71e73c6165eab783df9e26590069953e3c30" +dependencies = [ + "fxhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec 2.3.1", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec 3.7.5", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "is-terminal" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "8.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +dependencies = [ + "base64 0.21.7", + "pem", + "ring 0.16.20", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "lalrpop" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" +dependencies = [ + "ascii-canvas", + "bit-set 0.5.3", + "ena", + "itertools 0.11.0", + "lalrpop-util", + "petgraph", + "regex", + "regex-syntax", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", + "walkdir", +] + +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin 0.9.8", +] + +[[package]] +name = "libc" +version = "0.2.173" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.9.1", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest 0.10.7", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "open-fastrlp" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", + "ethereum-types 0.14.1", + "open-fastrlp-derive", +] + +[[package]] +name = "open-fastrlp-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" +dependencies = [ + "bytes", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec", + "bitvec 0.20.4", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive 2.3.1", + "serde", +] + +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec 1.0.1", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive 3.7.5", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "path-slash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", + "hmac", + "password-hash", + "sha2", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac", +] + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +dependencies = [ + "memchr", + "thiserror 2.0.12", + "ucd-trie", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version 0.4.1", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" +dependencies = [ + "proc-macro2", + "syn 2.0.103", +] + +[[package]] +name = "primitive-types" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06345ee39fbccfb06ab45f3a1a5798d9dafa04cb8921a76d227040003a234b0e" +dependencies = [ + "fixed-hash 0.7.0", + "impl-codec 0.5.1", + "impl-rlp", + "impl-serde 0.3.2", + "uint", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash 0.8.0", + "impl-codec 0.6.0", + "impl-rlp", + "impl-serde 0.4.0", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit 0.22.27", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" +dependencies = [ + "bit-set 0.8.0", + "bit-vec 0.8.0", + "bitflags 2.9.1", + "lazy_static", + "num-traits", + "rand 0.9.1", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "revm" +version = "14.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "641702b12847f9ed418d552f4fcabe536d867a2c980e96b6e7e25d7b992f929f" +dependencies = [ + "auto_impl", + "cfg-if", + "dyn-clone", + "ethers-core", + "ethers-providers", + "revm-interpreter", + "revm-precompile", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "revm-interpreter" +version = "10.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5e14002afae20b5bf1566f22316122f42f57517000e559c55b25bf7a49cba2" +dependencies = [ + "revm-primitives", + "serde", +] + +[[package]] +name = "revm-is-all-you-need" +version = "0.1.0" +dependencies = [ + "anyhow", + "bytes", + "chrono", + "colored 2.2.0", + "dotenv", + "eth-encode-packed", + "ethers", + "ethers-contract", + "ethers-core", + "ethers-providers", + "fern", + "futures", + "log", + "rand 0.8.5", + "revm", + "tokio", + "tokio-stream", + "wasm-bindgen", +] + +[[package]] +name = "revm-precompile" +version = "11.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3198c06247e8d4ad0d1312591edf049b0de4ddffa9fecb625c318fd67db8639b" +dependencies = [ + "aurora-engine-modexp", + "blst", + "c-kzg", + "cfg-if", + "k256", + "once_cell", + "revm-primitives", + "ripemd", + "secp256k1", + "sha2", + "substrate-bn", +] + +[[package]] +name = "revm-primitives" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f1525851a03aff9a9d6a1d018b414d76252d6802ab54695b27093ecd7e7a101" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", + "auto_impl", + "bitflags 2.9.1", + "bitvec 1.0.1", + "c-kzg", + "cfg-if", + "dyn-clone", + "enumn", + "hex", + "serde", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rlp-derive", + "rustc-hex", +] + +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ruint" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11256b5fe8c68f56ac6f39ef0720e592f33d2367a4782740d9c9142e889c7fb4" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec 3.7.5", + "primitive-types 0.12.2", + "proptest", + "rand 0.8.5", + "rand 0.9.1", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-demangle" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.26", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring 0.17.14", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring 0.17.14", + "untrusted 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scale-info" +version = "2.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" +dependencies = [ + "cfg-if", + "derive_more 1.0.0", + "parity-scale-codec 3.7.5", + "scale-info-derive", +] + +[[package]] +name = "scale-info-derive" +version = "2.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scrypt" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" +dependencies = [ + "hmac", + "pbkdf2 0.11.0", + "salsa20", + "sha2", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring 0.17.14", + "untrusted 0.9.0", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "rand 0.8.5", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.12", + "time", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "solang-parser" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c425ce1c59f4b154717592f0bdf4715c3a1d55058883622d3157e1f0908a5b26" +dependencies = [ + "itertools 0.11.0", + "lalrpop", + "lalrpop-util", + "phf", + "thiserror 1.0.69", + "unicode-xid", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.103", +] + +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand 0.8.5", + "rustc-hex", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "svm-rs" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11297baafe5fa0c99d5722458eac6a5e25c01eb1b8e5cd137f54079093daa7a4" +dependencies = [ + "dirs", + "fs2", + "hex", + "once_cell", + "reqwest", + "semver 1.0.26", + "serde", + "serde_json", + "sha2", + "thiserror 1.0.69", + "url", + "zip", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +dependencies = [ + "futures-util", + "log", + "rustls", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots", +] + +[[package]] +name = "tokio-util" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow 0.7.11", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.8.5", + "rustls", + "sha1", + "thiserror 1.0.69", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom 0.2.16", + "serde", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.103", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "ws_stream_wasm" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c173014acad22e83f16403ee360115b38846fe754e735c5d9d3803fe70c6abc" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version 0.4.1", + "send_wrapper 0.6.0", + "thiserror 2.0.12", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2 0.11.0", + "sha1", + "time", + "zstd", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.15+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/basic/85-REVM/Cargo.toml b/basic/85-REVM/Cargo.toml new file mode 100644 index 000000000..27d1e5c95 --- /dev/null +++ b/basic/85-REVM/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "revm-is-all-you-need" +version = "0.1.0" +edition = "2021" + +[dependencies] +rand = "0.8.5" +bytes = "1.2.1" +dotenv = "0.15.0" +futures = "0.3.5" +anyhow = "1.0.70" +tokio = { version = "1.29.0", features = ["full"] } +tokio-stream = { version = "0.1", features = ['sync'] } + +ethers-core = "2.0" +ethers-providers = "2.0" +ethers-contract = "2.0" +ethers = { version = "2.0", features = ["abigen", "ws"]} + +revm = { version = "14.0.1", features = ["ethersdb"] } + +wasm-bindgen = "0.2.88" + +eth-encode-packed = "0.1.0" + +colored = "2.0.0" +log = "0.4.17" +fern = {version = "0.6.2", features = ["colored"]} +chrono = "0.4.23" + + + diff --git a/basic/85-REVM/crytic-export/etherscan-contracts/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48-FiatTokenProxy.sol b/basic/85-REVM/crytic-export/etherscan-contracts/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48-FiatTokenProxy.sol new file mode 100644 index 000000000..41eab038d --- /dev/null +++ b/basic/85-REVM/crytic-export/etherscan-contracts/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48-FiatTokenProxy.sol @@ -0,0 +1,329 @@ +pragma solidity ^0.4.24; + +// File: zos-lib/contracts/upgradeability/Proxy.sol + +/** + * @title Proxy + * @dev Implements delegation of calls to other contracts, with proper + * forwarding of return values and bubbling of failures. + * It defines a fallback function that delegates all calls to the address + * returned by the abstract _implementation() internal function. + */ +contract Proxy { + /** + * @dev Fallback function. + * Implemented entirely in `_fallback`. + */ + function () payable external { + _fallback(); + } + + /** + * @return The Address of the implementation. + */ + function _implementation() internal view returns (address); + + /** + * @dev Delegates execution to an implementation contract. + * This is a low level function that doesn't return to its internal call site. + * It will return to the external caller whatever the implementation returns. + * @param implementation Address to delegate. + */ + function _delegate(address implementation) internal { + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, calldatasize) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize) + + switch result + // delegatecall returns 0 on error. + case 0 { revert(0, returndatasize) } + default { return(0, returndatasize) } + } + } + + /** + * @dev Function that is run as the first thing in the fallback function. + * Can be redefined in derived contracts to add functionality. + * Redefinitions must call super._willFallback(). + */ + function _willFallback() internal { + } + + /** + * @dev fallback implementation. + * Extracted to enable manual triggering. + */ + function _fallback() internal { + _willFallback(); + _delegate(_implementation()); + } +} + +// File: openzeppelin-solidity/contracts/AddressUtils.sol + +/** + * Utility library of inline functions on addresses + */ +library AddressUtils { + + /** + * Returns whether the target address is a contract + * @dev This function will return false if invoked during the constructor of a contract, + * as the code is not actually created until after the constructor finishes. + * @param addr address to check + * @return whether the target address is a contract + */ + function isContract(address addr) internal view returns (bool) { + uint256 size; + // XXX Currently there is no better way to check if there is a contract in an address + // than to check the size of the code at that address. + // See https://ethereum.stackexchange.com/a/14016/36603 + // for more details about how this works. + // TODO Check this again before the Serenity release, because all addresses will be + // contracts then. + // solium-disable-next-line security/no-inline-assembly + assembly { size := extcodesize(addr) } + return size > 0; + } + +} + +// File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol + +/** + * @title UpgradeabilityProxy + * @dev This contract implements a proxy that allows to change the + * implementation address to which it will delegate. + * Such a change is called an implementation upgrade. + */ +contract UpgradeabilityProxy is Proxy { + /** + * @dev Emitted when the implementation is upgraded. + * @param implementation Address of the new implementation. + */ + event Upgraded(address implementation); + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is + * validated in the constructor. + */ + bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3; + + /** + * @dev Contract constructor. + * @param _implementation Address of the initial implementation. + */ + constructor(address _implementation) public { + assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation")); + + _setImplementation(_implementation); + } + + /** + * @dev Returns the current implementation. + * @return Address of the current implementation + */ + function _implementation() internal view returns (address impl) { + bytes32 slot = IMPLEMENTATION_SLOT; + assembly { + impl := sload(slot) + } + } + + /** + * @dev Upgrades the proxy to a new implementation. + * @param newImplementation Address of the new implementation. + */ + function _upgradeTo(address newImplementation) internal { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Sets the implementation address of the proxy. + * @param newImplementation Address of the new implementation. + */ + function _setImplementation(address newImplementation) private { + require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address"); + + bytes32 slot = IMPLEMENTATION_SLOT; + + assembly { + sstore(slot, newImplementation) + } + } +} + +// File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol + +/** + * @title AdminUpgradeabilityProxy + * @dev This contract combines an upgradeability proxy with an authorization + * mechanism for administrative tasks. + * All external functions in this contract must be guarded by the + * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity + * feature proposal that would enable this to be done automatically. + */ +contract AdminUpgradeabilityProxy is UpgradeabilityProxy { + /** + * @dev Emitted when the administration has been transferred. + * @param previousAdmin Address of the previous admin. + * @param newAdmin Address of the new admin. + */ + event AdminChanged(address previousAdmin, address newAdmin); + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is + * validated in the constructor. + */ + bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b; + + /** + * @dev Modifier to check whether the `msg.sender` is the admin. + * If it is, it will run the function. Otherwise, it will delegate the call + * to the implementation. + */ + modifier ifAdmin() { + if (msg.sender == _admin()) { + _; + } else { + _fallback(); + } + } + + /** + * Contract constructor. + * It sets the `msg.sender` as the proxy administrator. + * @param _implementation address of the initial implementation. + */ + constructor(address _implementation) UpgradeabilityProxy(_implementation) public { + assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin")); + + _setAdmin(msg.sender); + } + + /** + * @return The address of the proxy admin. + */ + function admin() external view ifAdmin returns (address) { + return _admin(); + } + + /** + * @return The address of the implementation. + */ + function implementation() external view ifAdmin returns (address) { + return _implementation(); + } + + /** + * @dev Changes the admin of the proxy. + * Only the current admin can call this function. + * @param newAdmin Address to transfer proxy administration to. + */ + function changeAdmin(address newAdmin) external ifAdmin { + require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); + emit AdminChanged(_admin(), newAdmin); + _setAdmin(newAdmin); + } + + /** + * @dev Upgrade the backing implementation of the proxy. + * Only the admin can call this function. + * @param newImplementation Address of the new implementation. + */ + function upgradeTo(address newImplementation) external ifAdmin { + _upgradeTo(newImplementation); + } + + /** + * @dev Upgrade the backing implementation of the proxy and call a function + * on the new implementation. + * This is useful to initialize the proxied contract. + * @param newImplementation Address of the new implementation. + * @param data Data to send as msg.data in the low level call. + * It should include the signature and the parameters of the function to be + * called, as described in + * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding. + */ + function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin { + _upgradeTo(newImplementation); + require(address(this).call.value(msg.value)(data)); + } + + /** + * @return The admin slot. + */ + function _admin() internal view returns (address adm) { + bytes32 slot = ADMIN_SLOT; + assembly { + adm := sload(slot) + } + } + + /** + * @dev Sets the address of the proxy admin. + * @param newAdmin Address of the new proxy admin. + */ + function _setAdmin(address newAdmin) internal { + bytes32 slot = ADMIN_SLOT; + + assembly { + sstore(slot, newAdmin) + } + } + + /** + * @dev Only fall back when the sender is not the admin. + */ + function _willFallback() internal { + require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin"); + super._willFallback(); + } +} + +// File: contracts/FiatTokenProxy.sol + +/** +* Copyright CENTRE SECZ 2018 +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is furnished to +* do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +pragma solidity ^0.4.24; + + +/** + * @title FiatTokenProxy + * @dev This contract proxies FiatToken calls and enables FiatToken upgrades +*/ +contract FiatTokenProxy is AdminUpgradeabilityProxy { + constructor(address _implementation) public AdminUpgradeabilityProxy(_implementation) { + } +} \ No newline at end of file diff --git a/basic/85-REVM/crytic-export/etherscan-contracts/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2-WETH9.sol b/basic/85-REVM/crytic-export/etherscan-contracts/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2-WETH9.sol new file mode 100644 index 000000000..abcf5cfed --- /dev/null +++ b/basic/85-REVM/crytic-export/etherscan-contracts/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2-WETH9.sol @@ -0,0 +1,756 @@ +// Copyright (C) 2015, 2016, 2017 Dapphub + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pragma solidity ^0.4.18; + +contract WETH9 { + string public name = "Wrapped Ether"; + string public symbol = "WETH"; + uint8 public decimals = 18; + + event Approval(address indexed src, address indexed guy, uint wad); + event Transfer(address indexed src, address indexed dst, uint wad); + event Deposit(address indexed dst, uint wad); + event Withdrawal(address indexed src, uint wad); + + mapping (address => uint) public balanceOf; + mapping (address => mapping (address => uint)) public allowance; + + function() public payable { + deposit(); + } + function deposit() public payable { + balanceOf[msg.sender] += msg.value; + Deposit(msg.sender, msg.value); + } + function withdraw(uint wad) public { + require(balanceOf[msg.sender] >= wad); + balanceOf[msg.sender] -= wad; + msg.sender.transfer(wad); + Withdrawal(msg.sender, wad); + } + + function totalSupply() public view returns (uint) { + return this.balance; + } + + function approve(address guy, uint wad) public returns (bool) { + allowance[msg.sender][guy] = wad; + Approval(msg.sender, guy, wad); + return true; + } + + function transfer(address dst, uint wad) public returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom(address src, address dst, uint wad) + public + returns (bool) + { + require(balanceOf[src] >= wad); + + if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) { + require(allowance[src][msg.sender] >= wad); + allowance[src][msg.sender] -= wad; + } + + balanceOf[src] -= wad; + balanceOf[dst] += wad; + + Transfer(src, dst, wad); + + return true; + } +} + + +/* + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +*/ \ No newline at end of file diff --git a/basic/85-REVM/src/constants.rs b/basic/85-REVM/src/constants.rs new file mode 100644 index 000000000..84b0fb39f --- /dev/null +++ b/basic/85-REVM/src/constants.rs @@ -0,0 +1,33 @@ +use ethers::{ + prelude::Lazy, + types::{Address, Bytes}, +}; +use std::str::FromStr; + +pub fn get_env(key: &str) -> String { + std::env::var(key).unwrap() +} + +#[derive(Debug, Clone)] +pub struct Env { + pub https_url: String, + pub wss_url: String, +} + +impl Env { + pub fn new() -> Self { + Env { + https_url: get_env("HTTPS_URL"), + wss_url: get_env("WSS_URL"), + } + } +} + +pub static ZERO_ADDRESS: Lazy
= + Lazy::new(|| Address::from_str("0x0000000000000000000000000000000000000000").unwrap()); + +pub static SIMULATOR_CODE: Lazy = Lazy::new(|| { + "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063054d50d41461003b57806364bfce6f14610061575b600080fd5b61004e6100493660046106e4565b610089565b6040519081526020015b60405180910390f35b61007461006f36600461072c565b6101ae565b60408051928352602083019190915201610058565b60008084116100f35760405162461bcd60e51b815260206004820152602b60248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4960448201526a1394155517d05353d5539560aa1b60648201526084015b60405180910390fd5b6000831180156101035750600082115b6101605760405162461bcd60e51b815260206004820152602860248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4c604482015267495155494449545960c01b60648201526084016100ea565b600061016e856103e561078f565b9050600061017c848361078f565b905060008261018d876103e861078f565b61019791906107a6565b90506101a381836107b9565b979650505050505050565b6000806101c56001600160a01b03851686886104ef565b600080600080886001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015610209573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061022d91906107f2565b506001600160701b031691506001600160701b03169150866001600160a01b0316886001600160a01b0316101561026957819350809250610270565b8093508192505b50506040516370a0823160e01b81526001600160a01b03888116600483015260009184918916906370a0823190602401602060405180830381865afa1580156102bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102e19190610842565b6102eb919061085b565b604051630153543560e21b8152600481018290526024810185905260448101849052909150309063054d50d490606401602060405180830381865afa158015610338573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061035c9190610842565b6040516370a0823160e01b81523060048201529095506000906001600160a01b038816906370a0823190602401602060405180830381865afa1580156103a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ca9190610842565b9050600080886001600160a01b03168a6001600160a01b0316106103f0578760006103f4565b6000885b6040805160008152602081019182905263022c0d9f60e01b90915291935091506001600160a01b038c169063022c0d9f906104389085908590309060248101610892565b600060405180830381600087803b15801561045257600080fd5b505af1158015610466573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201528592506001600160a01b038c1691506370a0823190602401602060405180830381865afa1580156104b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104d49190610842565b6104de919061085b565b965050505050505094509492505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610541908490610546565b505050565b600061055b6001600160a01b038416836105a9565b9050805160001415801561058057508080602001905181019061057e91906108e2565b155b1561054157604051635274afe760e01b81526001600160a01b03841660048201526024016100ea565b60606105b7838360006105c0565b90505b92915050565b6060814710156105e55760405163cd78605960e01b81523060048201526024016100ea565b600080856001600160a01b031684866040516106019190610904565b60006040518083038185875af1925050503d806000811461063e576040519150601f19603f3d011682016040523d82523d6000602084013e610643565b606091505b509150915061065386838361065f565b925050505b9392505050565b6060826106745761066f826106bb565b610658565b815115801561068b57506001600160a01b0384163b155b156106b457604051639996b31560e01b81526001600160a01b03851660048201526024016100ea565b5080610658565b8051156106cb5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6000806000606084860312156106f957600080fd5b505081359360208301359350604090920135919050565b80356001600160a01b038116811461072757600080fd5b919050565b6000806000806080858703121561074257600080fd5b8435935061075260208601610710565b925061076060408601610710565b915061076e60608601610710565b905092959194509250565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176105ba576105ba610779565b808201808211156105ba576105ba610779565b6000826107d657634e487b7160e01b600052601260045260246000fd5b500490565b80516001600160701b038116811461072757600080fd5b60008060006060848603121561080757600080fd5b610810846107db565b925061081e602085016107db565b9150604084015163ffffffff8116811461083757600080fd5b809150509250925092565b60006020828403121561085457600080fd5b5051919050565b818103818111156105ba576105ba610779565b60005b83811015610889578181015183820152602001610871565b50506000910152565b84815283602082015260018060a01b038316604082015260806060820152600082518060808401526108cb8160a085016020870161086e565b601f01601f19169190910160a00195945050505050565b6000602082840312156108f457600080fd5b8151801515811461065857600080fd5b6000825161091681846020870161086e565b919091019291505056fea26469706673582212201d6da94f2d6ac0535f5153da5aac14a1f6ef19d15801986cfe2b2d6fab019c6564736f6c63430008140033" + .parse() + .unwrap() +}); \ No newline at end of file diff --git a/basic/85-REVM/src/eth_call_examples.rs b/basic/85-REVM/src/eth_call_examples.rs new file mode 100644 index 000000000..af088fbf5 --- /dev/null +++ b/basic/85-REVM/src/eth_call_examples.rs @@ -0,0 +1,86 @@ +use anyhow::{anyhow, Result}; +use ethers::{ + abi::{self, parse_abi}, + core::utils::keccak256, + prelude::*, + providers::{call_raw::RawCall, Provider, Ws}, + types::{spoof, TransactionRequest, H160, U256}, +}; +use log::info; +use std::{str::FromStr, sync::Arc}; + +use crate::constants::SIMULATOR_CODE; + +pub async fn eth_call_v2_simulate_swap( + provider: Arc>, + account: H160, + target_pair: H160, + input_token: H160, + output_token: H160, + input_balance_slot: i32, +) -> Result<(U256, U256)> { + // Shows how you can spoof multiple storage slots + // but also shows that you can only test one transaction at a time + let block = provider + .get_block(BlockNumber::Latest) + .await? + .ok_or(anyhow!("failed to retrieve block"))?; + + let ten_eth = U256::from(10) + .checked_mul(U256::from(10).pow(U256::from(18))) + .unwrap(); + + // Spoof user balance with 10 ETH (for gas fees) + let mut state = spoof::state(); + state.account(account).balance(ten_eth).nonce(0.into()); + + // Create Simulator contract with bytecode injection + let simulator_address = H160::from_str("0xF2d01Ee818509a9540d8324a5bA52329af27D19E").unwrap(); + state + .account(simulator_address) + .code((*SIMULATOR_CODE).clone()); + + // Spoof simulator input token balance + let input_balance_slot = keccak256(&abi::encode(&[ + abi::Token::Address(simulator_address), + abi::Token::Uint(U256::from(input_balance_slot)), + ])); + state.account(input_token).store( + input_balance_slot.into(), + H256::from_low_u64_be(ten_eth.as_u64()), + ); + + let one_eth = ten_eth.checked_div(U256::from(10)).unwrap(); + let simulator_abi = BaseContract::from( + parse_abi(&[ + "function v2SimulateSwap(uint256,address,address,address) external returns (uint256, uint256)", + ])? + ); + let calldata = simulator_abi.encode( + "v2SimulateSwap", + (one_eth, target_pair, input_token, output_token), + )?; + + let gas_price = U256::from(100) + .checked_mul(U256::from(10).pow(U256::from(9))) + .unwrap(); + let tx = TransactionRequest::default() + .from(account) + .to(simulator_address) + .value(U256::zero()) + .data(calldata.0) + .nonce(U256::zero()) + .gas(5000000) + .gas_price(gas_price) + .chain_id(1) + .into(); + let result = provider + .call_raw(&tx) + .state(&state) + .block(block.number.unwrap().into()) + .await?; + let out: (U256, U256) = simulator_abi.decode_output("v2SimulateSwap", result)?; + info!("v2SimulateSwap eth_call result: {:?}", out); + + Ok(out) +} \ No newline at end of file diff --git a/basic/85-REVM/src/foundry_examples.rs b/basic/85-REVM/src/foundry_examples.rs new file mode 100644 index 000000000..fb837f091 --- /dev/null +++ b/basic/85-REVM/src/foundry_examples.rs @@ -0,0 +1,126 @@ +use anyhow::{anyhow, Result}; +use ethers::{ + abi::{self, parse_abi}, + prelude::*, + providers::Middleware, + types::{BlockNumber, H160, U256}, +}; +use foundry::{ + executor::{ + fork::{BlockchainDb, BlockchainDbMeta, SharedBackend}, + Bytecode, TransactTo, TxEnv, + }, + revm::{ + db::CacheDB, + primitives::{keccak256, AccountInfo, U256 as rU256}, + EVM, + }, +}; +use log::info; +use std::{collections::BTreeSet, str::FromStr, sync::Arc}; + +use crate::constants::SIMULATOR_CODE; +use crate::revm_examples::get_tx_result; + +pub async fn foundry_v2_simulate_swap( + provider: Arc, + account: H160, + target_pair: H160, + input_token: H160, + output_token: H160, + input_balance_slot: i32, +) -> Result<(U256, U256)> { + let block = provider + .get_block(BlockNumber::Latest) + .await? + .ok_or(anyhow!("failed to retrieve block"))?; + +let shared_backend = SharedBackend::spawn_backend_thread( + provider.clone(), + BlockchainDb::new( + BlockchainDbMeta { + cfg_env: Default::default(), + block_env: Default::default(), + hosts: BTreeSet::from(["".to_string()]), + }, + None, + ), + Some(block.number.unwrap().into()), +); +let db = CacheDB::new(shared_backend); + +let mut evm = EVM::new(); +evm.database(db); + +evm.env.cfg.limit_contract_code_size = Some(0x100000); +evm.env.cfg.disable_block_gas_limit = true; +evm.env.cfg.disable_base_fee = true; + +evm.env.block.number = rU256::from(block.number.unwrap().as_u64() + 1); + +let fork_db = evm.db.as_mut().unwrap(); + +let ten_eth = rU256::from(10) + .checked_mul(rU256::from(10).pow(rU256::from(18))) + .unwrap(); + +// Set user: give the user enough ETH to pay for gas +let user_acc_info = AccountInfo::new(ten_eth, 0, Bytecode::default()); +fork_db.insert_account_info(account.into(), user_acc_info); + +// Deploy Simulator contract +let simulator_address = H160::from_str("0xF2d01Ee818509a9540d8324a5bA52329af27D19E").unwrap(); +let simulator_acc_info = AccountInfo::new( + rU256::ZERO, + 0, + Bytecode::new_raw((*SIMULATOR_CODE.0).into()), +); +fork_db.insert_account_info(simulator_address.into(), simulator_acc_info); + +let balance_slot = keccak256(&abi::encode(&[ + abi::Token::Address(simulator_address.into()), + abi::Token::Uint(U256::from(input_balance_slot)), +])); +fork_db.insert_account_storage(input_token.into(), balance_slot.into(), ten_eth)?; + +// run v2SimulateSwap +let amount_in = U256::from(1) + .checked_mul(U256::from(10).pow(U256::from(18))) + .unwrap(); +let simulator_abi = BaseContract::from( + parse_abi(&[ + "function v2SimulateSwap(uint256,address,address,address) external returns (uint256, uint256)", + ])? +); +let calldata = simulator_abi.encode( + "v2SimulateSwap", + (amount_in, target_pair, input_token, output_token), +)?; + +let gas_price = rU256::from(100) + .checked_mul(rU256::from(10).pow(rU256::from(9))) + .unwrap(); +let v2_simulate_swap_tx = TxEnv { + caller: account.into(), + gas_limit: 5000000, + gas_price: gas_price, + gas_priority_fee: None, + transact_to: TransactTo::Call(simulator_address.into()), + value: rU256::ZERO, + data: calldata.0, + chain_id: None, + nonce: None, + access_list: Default::default(), +}; +evm.env.tx = v2_simulate_swap_tx; + +let result = match evm.transact_commit() { + Ok(result) => result, + Err(e) => return Err(anyhow!("EVM call failed: {:?}", e)), +}; +let result = get_tx_result(result)?; +let out: (U256, U256) = simulator_abi.decode_output("v2SimulateSwap", result.output)?; +info!("Amount out: {:?}", out); + +Ok(out) +} \ No newline at end of file diff --git a/basic/85-REVM/src/lib.rs b/basic/85-REVM/src/lib.rs new file mode 100644 index 000000000..5ddce737b --- /dev/null +++ b/basic/85-REVM/src/lib.rs @@ -0,0 +1,7 @@ +pub mod revm_examples; +pub mod constants; +pub mod utils; +pub mod trace; +pub mod tokens; +// pub mod foundry_examples; // 暂时注释掉,因为 foundry-evm 版本兼容性问题 +pub mod eth_call_examples; diff --git a/basic/85-REVM/src/main.rs b/basic/85-REVM/src/main.rs new file mode 100644 index 000000000..fd0328f8c --- /dev/null +++ b/basic/85-REVM/src/main.rs @@ -0,0 +1,125 @@ +use anyhow::Result; +use ethers::{ + providers::{Provider, Ws, Middleware}, + types::{H160, BlockNumber}, +}; +use log::info; +use std::{str::FromStr, sync::Arc}; + +use revm_is_all_you_need::constants::Env; +use revm_is_all_you_need::utils::setup_logger; +use revm_is_all_you_need::tokens::get_implementation; + +use revm_is_all_you_need::revm_examples::{ + create_evm_instance, evm_env_setup, get_token_balance, geth_and_revm_tracing, + revm_contract_deploy_and_tracing, revm_v2_simulate_swap +}; + +use revm_is_all_you_need::eth_call_examples::eth_call_v2_simulate_swap; + +#[tokio::main] +async fn main() -> Result<()> { + dotenv::dotenv().ok(); + setup_logger()?; + + let mut evm = create_evm_instance(); + evm_env_setup(&mut evm); + + let user = H160::from_str("0xE2b5A9c1e325511a227EF527af38c3A7B65AFA1d").unwrap(); + + let weth = H160::from_str("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2").unwrap(); + let usdt = H160::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap(); + let _usdc = H160::from_str("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48").unwrap(); + let _dai = H160::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F").unwrap(); + + let weth_balance = get_token_balance(&mut evm, weth, user); + match weth_balance { + Ok(balance) => info!("WETH balance: {}", balance), + Err(e) => info!("WETH balance query failed (expected in empty EVM): {}", e), + } + + // add this 👇 + let env = Env::new(); + info!("Attempting to connect to: {}", env.wss_url); + + match Ws::connect(&env.wss_url).await { + Ok(ws) => { + let provider = Arc::new(Provider::new(ws)); + + match geth_and_revm_tracing(&mut evm, provider.clone(), weth, user).await { + Ok(_) => info!("Tracing completed successfully"), + Err(e) => info!("Tracing error: {e:?}"), + } + match revm_contract_deploy_and_tracing(&mut evm, provider.clone(), weth, user).await { + Ok(_) => {} + Err(e) => info!("Tracing error: {e:?}"), + } + + let block = provider + .get_block(BlockNumber::Latest) + .await? + .ok_or_else(|| anyhow::anyhow!("Failed to get block"))?; + + let uniswap_v2_factory = H160::from_str("0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f").unwrap(); + let weth_usdt_pair = H160::from_str("0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852").unwrap(); + + let weth_balance_slot = + revm_contract_deploy_and_tracing(&mut evm, provider.clone(), weth, user) + .await + .unwrap(); + let usdt_balance_slot = + revm_contract_deploy_and_tracing(&mut evm, provider.clone(), usdt, user) + .await + .unwrap(); + + let weth_implementation = get_implementation(provider.clone(), weth, block.number.unwrap()) + .await + .unwrap(); + let usdt_implementation = get_implementation(provider.clone(), usdt, block.number.unwrap()) + .await + .unwrap(); + + info!("WETH proxy: {:?}", weth_implementation); + info!("USDT proxy: {:?}", usdt_implementation); + + match revm_v2_simulate_swap( + &mut evm, + provider.clone(), + user, + uniswap_v2_factory, + weth_usdt_pair, + weth, + usdt, + weth_balance_slot, + usdt_balance_slot, + weth_implementation, + usdt_implementation, + ) + .await + { + Ok(_) => info!("v2SimulateSwap revm completed successfully"), + Err(e) => info!("v2SimulateSwap revm failed: {e:?}"), + } + + match eth_call_v2_simulate_swap( + provider.clone(), + user, + weth_usdt_pair, + weth, + usdt, + weth_balance_slot, + ) + .await + { + Ok(_) => info!("v2SimulateSwap eth_call completed successfully"), + Err(e) => info!("v2SimulateSwap eth_call failed: {e:?}"), + } + } + Err(e) => { + info!("WebSocket connection failed: {e:?}"); + info!("This is expected when using demo endpoints with rate limits"); + } + } + + Ok(()) +} \ No newline at end of file diff --git a/basic/85-REVM/src/revm_examples.rs b/basic/85-REVM/src/revm_examples.rs new file mode 100644 index 000000000..95325c224 --- /dev/null +++ b/basic/85-REVM/src/revm_examples.rs @@ -0,0 +1,528 @@ +// add all the necessary imports 👇 +use anyhow::{anyhow, Result}; +use bytes::Bytes; +use ethers::{ + abi::{parse_abi}, + prelude::*, + types::{ + H160, U256, transaction::eip2930::AccessList, + }, +}; +use log::info; +use std::{sync::Arc, str::FromStr}; +use crate::trace::get_state_diff; +use crate::constants::SIMULATOR_CODE; +use revm::{ + db::{CacheDB, EmptyDB, InMemoryDB, EthersDB}, + primitives::{ + ExecutionResult, Log, Output, TransactTo, + Address, Bytes as rBytes, keccak256, U256 as rU256, + AccountInfo, Bytecode, TxEnv, + }, + Database, Evm, +}; + +// This will create a clean slate EVM environment that doesn't have any storage values in it. +// So no accounts or contracts exist yet. We'll have to deal with everything on our own. +pub fn create_evm_instance() -> Evm<'static, (), InMemoryDB> { + let db = CacheDB::new(EmptyDB::default()); + let evm = Evm::builder().with_db(db).build(); + evm +} + +// evm_env_setup is a function that overrides some default env values to make it more efficient for testing +pub fn evm_env_setup(evm: &mut Evm<'static, (), InMemoryDB>) { + // overriding some default env values to make it more efficient for testing + evm.context.evm.env.cfg.limit_contract_code_size = Some(0x100000); +} + +// add this 👇 +#[derive(Debug, Clone)] +pub struct TxResult { + pub output: Bytes, + pub logs: Option>, + pub gas_used: u64, + pub gas_refunded: u64, +} + +// get_token_balance +pub fn get_token_balance(evm: &mut Evm<'static, (), InMemoryDB>, token: H160, account: H160) -> Result { + let erc20_abi = BaseContract::from(parse_abi(&[ + "function balanceOf(address) external view returns (uint256)", + ])?); + let calldata = erc20_abi.encode("balanceOf", account)?; + + evm.context.evm.env.tx.caller = Address::from_slice(&account.0); + evm.context.evm.env.tx.transact_to = TransactTo::Call(Address::from_slice(&token.0)); + evm.context.evm.env.tx.data = rBytes::from(calldata.0); + + // This will fail, because the token contract has not been deployed yet + let result = match evm.transact() { + Ok(result) => result, + Err(e) => return Err(anyhow!("EVM call failed: {e:?}")), + }; + let tx_result = match result.result { + ExecutionResult::Success { + gas_used, + gas_refunded, + output, + logs, + .. + } => match output { + Output::Call(o) => TxResult { + output: o.into(), + logs: Some(logs), + gas_used, + gas_refunded, + }, + Output::Create(o, _) => TxResult { + output: o.into(), + logs: Some(logs), + gas_used, + gas_refunded, + }, + }, + ExecutionResult::Revert { gas_used, output } => { + return Err(anyhow!( + "EVM REVERT: {:?} / Gas used: {:?}", + output, + gas_used + )) + } + ExecutionResult::Halt { + reason, gas_used, .. + } => return Err(anyhow!("EVM HALT: {:?} / Gas used: {:?}", reason, gas_used)), + }; + let decoded_output = erc20_abi.decode_output("balanceOf", tx_result.output)?; + Ok(decoded_output) +} + + +pub async fn geth_and_revm_tracing( + _evm: &mut Evm<'static, (), InMemoryDB>, + provider: Arc, + token: H160, + account: H160, +) -> Result { + let erc20_abi = BaseContract::from(parse_abi(&[ + "function balanceOf(address) external view returns (uint256)", + ])?); + let calldata = erc20_abi.encode("balanceOf", account)?; + + let block = provider + .get_block(BlockNumber::Latest) + .await? + .ok_or(anyhow!("failed to retrieve block"))?; + let nonce = provider + .get_transaction_count(account, Some(BlockId::Number(BlockNumber::Latest))) + .await?; + let chain_id = provider.get_chainid().await?; + + let tx = Eip1559TransactionRequest { + chain_id: Some(chain_id.as_u64().into()), + nonce: Some(nonce), + from: Some(account), + to: Some(NameOrAddress::Address(token)), + gas: None, + value: None, + data: Some(calldata), + max_priority_fee_per_gas: None, + max_fee_per_gas: None, + access_list: AccessList::default(), + }; + let geth_trace = get_state_diff(provider.clone(), tx, block.number.unwrap()).await?; + let prestate = match geth_trace { + GethTrace::Known(known) => match known { + GethTraceFrame::PreStateTracer(prestate) => match prestate { + PreStateFrame::Default(prestate_mode) => Some(prestate_mode), + _ => None, + }, + _ => None, + }, + _ => None, + } + .unwrap(); + let geth_touched_accs = prestate.0.keys(); + info!("Geth trace: {:?}", geth_touched_accs); + + let token_acc_state = prestate.0.get(&token).ok_or(anyhow!("no token key"))?; + let token_touched_storage = token_acc_state + .storage + .clone() + .ok_or(anyhow!("no storage values"))?; + + for i in 0..20 { + let slot = keccak256(&abi::encode(&[ + abi::Token::Address(account.into()), + abi::Token::Uint(U256::from(i)), + ])); + info!("{} 0x{:x}", i, H256::from(slot.0)); + match token_touched_storage.get(&H256::from(slot.0)) { + Some(_) => { + info!("Balance storage slot: {:?} (0x{:x})", i, H256::from(slot.0)); + return Ok(i); + } + None => {} + } + } + + Ok(0) +} + +// add this 👇 +pub async fn revm_contract_deploy_and_tracing( + _evm: &mut Evm<'static, (), InMemoryDB>, + provider: Arc, + token: H160, + account: H160, +) -> Result { + // deploy contract to EVM + let block = provider + .get_block(BlockNumber::Latest) + .await? + .ok_or(anyhow!("failed to retrieve block"))?; + + let ethersdb = EthersDB::new(provider.clone(), Some(block.number.unwrap().into())) + .ok_or_else(|| anyhow!("Failed to create EthersDB"))?; + + let token_address = Address::from_slice(&token.0); + + // 我们将创建一个新的 EVM 实例与 EthersDB + let mut new_evm = Evm::builder() + .with_db(ethersdb) + .build(); + + let erc20_abi = BaseContract::from(parse_abi(&[ + "function balanceOf(address) external view returns (uint256)", + ])?); + let calldata = erc20_abi.encode("balanceOf", account)?; + + new_evm.context.evm.env.tx.caller = Address::from_slice(&account.0); + new_evm.context.evm.env.tx.transact_to = TransactTo::Call(token_address); + new_evm.context.evm.env.tx.data = rBytes::from(calldata.0.clone()); + + let result = match new_evm.transact() { + Ok(result) => result, + Err(e) => return Err(anyhow!("EVM call failed: {e:?}")), + }; + + // 从结果中获取状态变化 + let state_changes = match result.result { + ExecutionResult::Success { logs, .. } => { + info!("Transaction successful, logs: {:?}", logs); + result.state + } + ExecutionResult::Revert { output, .. } => { + info!("Transaction reverted: {:?}", output); + result.state + } + ExecutionResult::Halt { reason, .. } => { + info!("Transaction halted: {:?}", reason); + result.state + } + }; + + // 检查 token 合约的存储变化 + if let Some(account_state) = state_changes.get(&token_address) { + info!("Found token account state changes"); + let storage = &account_state.storage; + + // 尝试查找存储槽 - 与 geth_and_revm_tracing 使用相同的逻辑 + for i in 0..20 { + let slot = keccak256(&abi::encode(&[ + abi::Token::Address(account.into()), + abi::Token::Uint(U256::from(i)), + ])); + let slot_u256 = rU256::from_be_bytes(slot.0); + info!("Checking storage slot {}: 0x{:x}", i, H256::from(slot.0)); + + // 检查这个存储槽是否被访问/修改 + if storage.contains_key(&slot_u256) { + info!("Balance storage slot found: {:?} (0x{:x})", i, H256::from(slot.0)); + return Ok(i); + } + } + } else { + info!("No state changes found for token address: {:?}", token_address); + } + + Ok(0) +} + +// add this 👇 +pub async fn revm_v2_simulate_swap( + _evm: &mut Evm<'static, (), InMemoryDB>, + provider: Arc, + account: H160, + factory: H160, + target_pair: H160, + input_token: H160, + output_token: H160, + input_balance_slot: i32, + output_balance_slot: i32, + input_token_implementation: Option, + output_token_implementation: Option, +) -> Result<(U256, U256)> { + // add this 👇 + let block = provider + .get_block(BlockNumber::Latest) + .await? + .ok_or(anyhow!("failed to retrieve block"))?; + + let ethersdb = EthersDB::new(provider.clone(), Some(block.number.unwrap().into())) + .ok_or_else(|| anyhow!("Failed to create EthersDB"))?; + + let ten_eth = rU256::from(10) + .checked_mul(rU256::from(10).pow(rU256::from(18))) + .unwrap(); + + // 创建新的 EVM 实例与 EthersDB + let mut new_evm = Evm::builder() + .with_db(CacheDB::new(ethersdb)) + .build(); + + // Set user: give the user enough ETH to pay for gas + let user_acc_info = AccountInfo::new(ten_eth, 0, keccak256(&[]), Bytecode::default()); + new_evm.db_mut().insert_account_info(Address::from_slice(&account.0), user_acc_info); + + // Deploy Simulator contract + let simulator_address = H160::from_str("0xF2d01Ee818509a9540d8324a5bA52329af27D19E").unwrap(); + let simulator_acc_info = AccountInfo::new( + rU256::ZERO, + 0, + keccak256(&[]), + Bytecode::new_raw(rBytes::from(SIMULATOR_CODE.0.to_vec())), + ); + new_evm.db_mut().insert_account_info(Address::from_slice(&simulator_address.0), simulator_acc_info); + + // Deploy necessary contracts to simulate Uniswap V2 swap + let input_token_address = match input_token_implementation { + Some(implementation) => implementation, + None => input_token, + }; + let output_token_address = match output_token_implementation { + Some(implementation) => implementation, + None => output_token, + }; + + // 获取账户信息 + let input_token_acc_info = new_evm.db_mut().basic(Address::from_slice(&input_token_address.0))?.unwrap_or_default(); + let output_token_acc_info = new_evm.db_mut().basic(Address::from_slice(&output_token_address.0))?.unwrap_or_default(); + let factory_acc_info = new_evm.db_mut().basic(Address::from_slice(&factory.0))?.unwrap_or_default(); + + new_evm.db_mut().insert_account_info(Address::from_slice(&input_token.0), input_token_acc_info); + new_evm.db_mut().insert_account_info(Address::from_slice(&output_token.0), output_token_acc_info); + new_evm.db_mut().insert_account_info(Address::from_slice(&factory.0), factory_acc_info); + + // Deploy pair contract using factory + let factory_abi = BaseContract::from(parse_abi(&[ + "function createPair(address,address) external returns (address)", +])?); +let calldata = factory_abi.encode("createPair", (input_token, output_token))?; + +let gas_price = rU256::from(100) + .checked_mul(rU256::from(10).pow(rU256::from(9))) + .unwrap(); + +// Create a pair contract using the factory contract +let create_pair_tx = TxEnv { + caller: Address::from_slice(&account.0), + gas_limit: 5000000, + gas_price: gas_price, + gas_priority_fee: None, + transact_to: TransactTo::Call(Address::from_slice(&factory.0)), + value: rU256::ZERO, + data: rBytes::from(calldata.0), + chain_id: None, + nonce: None, + access_list: Default::default(), + blob_hashes: Vec::new(), + max_fee_per_blob_gas: None, + authorization_list: None, +}; +new_evm.context.evm.env.tx = create_pair_tx; + +let result = match new_evm.transact() { + Ok(result) => result, + Err(e) => return Err(anyhow!("EVM call failed: {:?}", e)), +}; + let result = get_tx_result(result.result)?; + let pair_address: H160 = factory_abi.decode_output("createPair", result.output)?; + info!("Pair created: {:?}", pair_address); + + // parse PairCreated event to get token0 / token1 + let _pair_created_log = &result.logs.unwrap()[0]; + // 简化处理:直接使用输入的 token 顺序 + let token0 = Address::from_slice(&input_token.0); + let token1 = Address::from_slice(&output_token.0); + info!("Token 0: {:?} / Token 1: {:?}", token0, token1); + + // Check if the target_pair is equal to the pair created address + assert_eq!(target_pair, pair_address); + + // There're no reserves in the pool, so we inject the reserves that we retrieve with ethersdb + // The storage slot of reserves is: 8 + let reserves_slot = rU256::from(8); + let original_reserves = new_evm.db_mut().storage(Address::from_slice(&pair_address.0), reserves_slot)?; + new_evm.db_mut().insert_account_storage(Address::from_slice(&pair_address.0), reserves_slot, original_reserves)?; + + // Check that the reserves are set correctly + let pair_abi = BaseContract::from(parse_abi(&[ + "function getReserves() external view returns (uint112,uint112,uint32)", + ])?); + let calldata = pair_abi.encode("getReserves", ())?; + let get_reserves_tx = TxEnv { + caller: Address::from_slice(&account.0), + gas_limit: 5000000, + gas_price: gas_price, + gas_priority_fee: None, + transact_to: TransactTo::Call(Address::from_slice(&target_pair.0)), + value: rU256::ZERO, + data: rBytes::from(calldata.0), + chain_id: None, + nonce: None, + access_list: Default::default(), + blob_hashes: Vec::new(), + max_fee_per_blob_gas: None, + authorization_list: None, + }; + new_evm.context.evm.env.tx = get_reserves_tx; + + let result = match new_evm.transact() { + Ok(result) => result, + Err(e) => return Err(anyhow!("EVM call failed: {:?}", e)), + }; + let result = get_tx_result(result.result)?; + let reserves: (U256, U256, U256) = pair_abi.decode_output("getReserves", result.output)?; + info!("Pair reserves: {:?}", reserves); + + // We actually have to feed the input/output token balance to pair contract (to perform real swaps) + let (balance_slot_0, balance_slot_1) = if token0 == Address::from_slice(&input_token.0) { + (input_balance_slot, output_balance_slot) + } else { + (output_balance_slot, input_balance_slot) + }; + info!( + "Balance slot 0: {:?} / slot 1: {:?}", + balance_slot_0, balance_slot_1 + ); + + let pair_token0_slot = keccak256(&abi::encode(&[ + abi::Token::Address(target_pair.into()), + abi::Token::Uint(U256::from(balance_slot_0)), + ])); + // 转换 ethers::U256 到 revm::U256 + let reserve0_str = reserves.0.to_string(); + let reserve0_revm = rU256::from_str_radix(&reserve0_str, 10).unwrap_or(rU256::ZERO); + new_evm.db_mut().insert_account_storage(token0, rU256::from_be_bytes(pair_token0_slot.0), reserve0_revm)?; + + let pair_token1_slot = keccak256(&abi::encode(&[ + abi::Token::Address(target_pair.into()), + abi::Token::Uint(U256::from(balance_slot_1)), + ])); + // 转换 ethers::U256 到 revm::U256 + let reserve1_str = reserves.1.to_string(); + let reserve1_revm = rU256::from_str_radix(&reserve1_str, 10).unwrap_or(rU256::ZERO); + new_evm.db_mut().insert_account_storage(token1, rU256::from_be_bytes(pair_token1_slot.0), reserve1_revm)?; + + // Check that balance is set correctly + let token_abi = BaseContract::from(parse_abi(&[ + "function balanceOf(address) external view returns (uint256)", + ])?); + for token in vec![token0, token1] { + let calldata = token_abi.encode("balanceOf", target_pair)?; + new_evm.context.evm.env.tx.caller = Address::from_slice(&account.0); + new_evm.context.evm.env.tx.transact_to = TransactTo::Call(token); + new_evm.context.evm.env.tx.data = rBytes::from(calldata.0); + let result = match new_evm.transact() { + Ok(result) => result, + Err(e) => return Err(anyhow!("EVM call failed: {:?}", e)), + }; + let result = get_tx_result(result.result)?; + let balance: U256 = token_abi.decode_output("balanceOf", result.output)?; + info!("{:?}: {:?}", token, balance); + } + + // feed simulator with input_token balance + let slot_in = keccak256(&abi::encode(&[ + abi::Token::Address(simulator_address.into()), + abi::Token::Uint(U256::from(input_balance_slot)), + ])); + new_evm.db_mut().insert_account_storage(Address::from_slice(&input_token.0), rU256::from_be_bytes(slot_in.0), ten_eth)?; + + // run v2SimulateSwap + let amount_in = U256::from(1) + .checked_mul(U256::from(10).pow(U256::from(18))) + .unwrap(); +let simulator_abi = BaseContract::from( + parse_abi(&[ + "function v2SimulateSwap(uint256,address,address,address) external returns (uint256, uint256)", + ])? +); +let calldata = simulator_abi.encode( + "v2SimulateSwap", + (amount_in, target_pair, input_token, output_token), +)?; +let v2_simulate_swap_tx = TxEnv { + caller: Address::from_slice(&account.0), + gas_limit: 5000000, + gas_price: gas_price, + gas_priority_fee: None, + transact_to: TransactTo::Call(Address::from_slice(&simulator_address.0)), + value: rU256::ZERO, + data: rBytes::from(calldata.0), + chain_id: None, + nonce: None, + access_list: Default::default(), + blob_hashes: Vec::new(), + max_fee_per_blob_gas: None, + authorization_list: None, +}; +new_evm.context.evm.env.tx = v2_simulate_swap_tx; + +let result = match new_evm.transact() { + Ok(result) => result, + Err(e) => return Err(anyhow!("EVM call failed: {:?}", e)), +}; +let result = get_tx_result(result.result)?; +let out: (U256, U256) = simulator_abi.decode_output("v2SimulateSwap", result.output)?; +info!("Amount out: {:?}", out); + + Ok(out) // 返回实际结果 + +} + +pub fn get_tx_result(result: ExecutionResult) -> Result { + let output = match result { + ExecutionResult::Success { + gas_used, + gas_refunded, + output, + logs, + .. + } => match output { + Output::Call(o) => TxResult { + output: Bytes::from(o.to_vec()), + logs: Some(logs), + gas_used, + gas_refunded, + }, + Output::Create(o, _) => TxResult { + output: Bytes::from(o.to_vec()), + logs: Some(logs), + gas_used, + gas_refunded, + }, + }, + ExecutionResult::Revert { gas_used, output } => { + return Err(anyhow!( + "EVM REVERT: {:?} / Gas used: {:?}", + output, + gas_used + )) + } + ExecutionResult::Halt { reason, .. } => return Err(anyhow!("EVM HALT: {:?}", reason)), + }; + + Ok(output) +} \ No newline at end of file diff --git a/basic/85-REVM/src/tokens.rs b/basic/85-REVM/src/tokens.rs new file mode 100644 index 000000000..7ab75fb93 --- /dev/null +++ b/basic/85-REVM/src/tokens.rs @@ -0,0 +1,52 @@ +use anyhow::Result; +use ethers::prelude::*; +use ethers_core::types::{BlockId, BlockNumber, TxHash, H160, U256}; +use std::sync::Arc; +use tokio::task::JoinSet; + +use crate::constants::ZERO_ADDRESS; + +pub async fn get_implementation( + provider: Arc, + token: H160, + block_number: U64, +) -> Result> { + // adapted from: https://github.com/gnosis/evm-proxy-detection/blob/main/src/index.ts + let eip_1967_logic_slot = + U256::from("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"); + let eip_1967_beacon_slot = + U256::from("0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50"); + let open_zeppelin_implementation_slot = + U256::from("0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3"); + let eip_1822_logic_slot = + U256::from("0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7"); + + let implementation_slots = vec![ + eip_1967_logic_slot, + eip_1967_beacon_slot, + open_zeppelin_implementation_slot, + eip_1822_logic_slot, + ]; + + let mut set = JoinSet::new(); + + for slot in implementation_slots { + let _provider = provider.clone(); + let fut = tokio::spawn(async move { + _provider + .get_storage_at(token, TxHash::from_uint(&slot), Some(block_number.into())) + .await + }); + set.spawn(fut); + } + + while let Some(res) = set.join_next().await { + let out = res???; + let implementation = H160::from(out); + if implementation != *ZERO_ADDRESS { + return Ok(Some(implementation)); + } + } + + Ok(None) +} \ No newline at end of file diff --git a/basic/85-REVM/src/trace.rs b/basic/85-REVM/src/trace.rs new file mode 100644 index 000000000..94ce8ed2a --- /dev/null +++ b/basic/85-REVM/src/trace.rs @@ -0,0 +1,34 @@ +use anyhow::Result; +use ethers::prelude::*; +use ethers_providers::Middleware; +use std::sync::Arc; + +pub async fn get_state_diff( + provider: Arc, + tx: Eip1559TransactionRequest, + block_number: U64, +) -> Result { + let trace = provider + .debug_trace_call( + tx, + Some(block_number.into()), + GethDebugTracingCallOptions { + tracing_options: GethDebugTracingOptions { + disable_storage: None, + disable_stack: None, + enable_memory: None, + enable_return_data: None, + tracer: Some(GethDebugTracerType::BuiltInTracer( + GethDebugBuiltInTracerType::PreStateTracer, + )), + tracer_config: None, + timeout: None, + }, + state_overrides: None, + block_overrides: None, + }, + ) + .await?; + + Ok(trace) +} \ No newline at end of file diff --git a/basic/85-REVM/src/utils.rs b/basic/85-REVM/src/utils.rs new file mode 100644 index 000000000..fdb799a9e --- /dev/null +++ b/basic/85-REVM/src/utils.rs @@ -0,0 +1,30 @@ +use anyhow::{self, Result}; +use fern::colors::{Color, ColoredLevelConfig}; +use log::LevelFilter; + +pub fn setup_logger() -> Result<()> { + let colors = ColoredLevelConfig { + trace: Color::Cyan, + debug: Color::Magenta, + info: Color::Green, + warn: Color::Red, + error: Color::BrightRed, + ..ColoredLevelConfig::new() + }; + + fern::Dispatch::new() + .format(move |out, message, record| { + out.finish(format_args!( + "{}[{}] {}", + chrono::Local::now().format("[%H:%M:%S]"), + colors.color(record.level()), + message + )) + }) + .chain(std::io::stdout()) + .level(log::LevelFilter::Error) + .level_for("revm_is_all_you_need", LevelFilter::Info) + .apply()?; + + Ok(()) +} \ No newline at end of file diff --git a/basic/85-REVM/usdc_storage.json b/basic/85-REVM/usdc_storage.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/basic/85-REVM/usdc_storage.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/basic/85-REVM/weth_storage.json b/basic/85-REVM/weth_storage.json new file mode 100644 index 000000000..20c782239 --- /dev/null +++ b/basic/85-REVM/weth_storage.json @@ -0,0 +1,47 @@ +{ + "name": { + "name": "name", + "type_string": "string", + "slot": 0, + "size": 256, + "offset": 0, + "value": null, + "elems": {} + }, + "symbol": { + "name": "symbol", + "type_string": "string", + "slot": 1, + "size": 256, + "offset": 0, + "value": null, + "elems": {} + }, + "decimals": { + "name": "decimals", + "type_string": "uint8", + "slot": 2, + "size": 8, + "offset": 0, + "value": null, + "elems": {} + }, + "balanceOf": { + "name": "balanceOf", + "type_string": "mapping(address => uint256)", + "slot": 3, + "size": 256, + "offset": 0, + "value": null, + "elems": {} + }, + "allowance": { + "name": "allowance", + "type_string": "mapping(address => mapping(address => uint256))", + "slot": 4, + "size": 256, + "offset": 0, + "value": null, + "elems": {} + } +} \ No newline at end of file