Bitcoin 钱包 Schnorr 离线地址生成与签名
1. Schnorr 签名
Schnorr 签名是一种数字签名方案,以其简洁性、高效性和安全性著称,并作为比特币的 Taproot 升级的一部分被采用。以下是 Schnorr 签名的详细流程和特点。
1.1. 签名流程
Schnorr 签名算法由以下步骤组成:
密钥生成
- 私钥 𝑘 是一个随机选择的整数,范围为 [1,𝑛−1],其中 𝑛 是椭圆曲线的阶。
- 公钥 𝑃 是通过将私钥乘以椭圆曲线的基点 𝐺G 得到的,即 𝑃=𝑘⋅𝐺。
签名生成
假设消息为 𝑚。
- 生成随机数 𝑟:选择一个随机数 𝑟r 作为临时私钥,范围为 [1,𝑛−1]。
- 计算非交互式挑战:计算 𝑅=𝑟⋅𝐺,其中 𝐺 是椭圆曲线的基点。
- 计算哈希值:计算哈希值 𝑒:
𝑒=H(𝑅∥𝑃∥𝑚)
- 其中 H 是一个哈希函数,如 SHA-256,𝑅∥𝑃∥𝑚 表示 𝑅、公钥 𝑃 和消息 𝑚 的连接。
- 计算签名:计算签名 𝑠:
𝑠=𝑟+𝑒⋅𝑘(mod𝑛)
- 其中 𝑘 是私钥,𝑒 是哈希值,𝑛n是椭圆曲线的阶。
签名由 (𝑅,𝑠)组成。
签名验证
- 计算哈希值:重新计算哈希值 𝑒:
𝑒=H(𝑅∥𝑃∥𝑚)
- 验证等式:验证以下等式是否成立:
𝑠⋅𝐺=𝑅+𝑒⋅𝑃
- 如果等式成立,签名是有效的;否则无效。
1.2.签名特点
简洁性
- Schnorr 签名算法结构简单,签名过程和验证过程都较为直观。这种简洁性有助于实现和分析安全性。
安全性
- 不可伪造:基于离散对数问题的困难性,Schnorr 签名在当前已知的计算能力下被认为是安全的。
- 欧氏证明:Schnorr 签名有严格的欧氏安全性证明。
高效性
- 计算效率:签名生成和验证的计算效率较高。尤其在批量签名验证中,Schnorr 签名具有显著的性能优势。
- 签名长度:Schnorr 签名的长度较短,相比于 ECDSA 签名,Schnorr 签名占用的存储空间更少。
扩展性和兼容性
Schnorr 签名支持多种扩展功能,如多重签名(MuSig)和聚合签名。这些扩展功能在增强隐私性和可扩展性方面表现出色。
- 多重签名(MuSig):Schnorr 签名允许多个签名者的公钥和签名聚合为单一的公钥和签名,这显著提高了多重签名的效率和隐私性。
- 兼容性:Schnorr 签名与现有的椭圆曲线加密标准兼容,便于在现有系统中实现和部署。
1.3. 签名算法 Python 代码实现
SchnorrSignObj 类
import hashlib
from ecdsa import SECP256k1, SigningKey, VerifyingKey
from ecdsa.curves import Curve
class SchnorrSignObj:
curve: Curve
def __init__(self):
# 椭圆曲线参数
self.curve = SECP256k1
def schnorr_sign(self, private_key, message):
# 生成私钥和公钥
signing_key = SigningKey.from_string(private_key, curve=self.curve)
verifying_key = signing_key.get_verifying_key()
# 生成随机数 r
r = SigningKey.generate(curve=self.curve).privkey.secret_multiplier
R = VerifyingKey.from_public_point(r * self.curve.generator, curve=self.curve)
# 计算哈希 e
e = hashlib.sha256(R.to_string() + verifying_key.to_string() + message).digest()
e = int.from_bytes(e, 'big')
# 计算签名 s
s = (r + e * signing_key.privkey.secret_multiplier) % self.curve.order
return R.to_string(), s.to_bytes(32, 'big')
def schnorr_verify(self, public_key, message, signature):
# 解析签名
R = VerifyingKey.from_string(signature[0], curve=self.curve)
s = int.from_bytes(signature[1], 'big')
# 计算哈希 e
e = hashlib.sha256(signature[0] + public_key.to_string() + message).digest()
e = int.from_bytes(e, 'big')
# 验证签名
sG = VerifyingKey.from_public_point(s * self.curve.generator, curve=self.curve)
ReP = VerifyingKey.from_public_point(R.pubkey.point + e * public_key.pubkey.point, curve=self.curve)
return sG.to_string() == ReP.to_string()
测试代码
import schnorr
from ecdsa import SECP256k1, SigningKey, VerifyingKey
# 初始化类
schnorr_test = schnorr.SchnorrSignObj()
# 产生密钥并对交易签名
private_key = SigningKey.generate(curve=SECP256k1).to_string()
message = b"Hello, Schnorr!"
signature = schnorr_test.schnorr_sign(private_key, message)
print("Signature:", signature)
# 验证交易签名
public_key = VerifyingKey.from_string(SigningKey.from_string(private_key, curve=SECP256k1).get_verifying_key().to_string(), curve=SECP256k1)
is_valid = schnorr_test.schnorr_verify(public_key, message, signature)
print("Signature valid:", is_valid)
2. BIP86 协议
2.1 结构路径 BIP86 是一种专门为生成 Taproot 地址的路径标准,简化并优化了生成单密钥 Taproot 地址的过程。BIP86 的路径结构如下:
m / 86' / coin_type' / account' / change / address_index
- Purpose: 固定为 86',表示遵循 BIP86 标准。
- Coin Type: 定义特定加密货币的类型,如 0' 表示比特币。
- Account: 账户索引,允许钱包管理多个独立账户。
- Change: 0 表示外部链(收款地址),1 表示内部链(找零地址)。
- Address Index: 用于生成具体地址的索引。
2.2.地址生成方法
BIP86 使用新的路径结构生成 Taproot 地址,通过椭圆曲线算法派生公钥,并将其编码为 Bech32 格式的地址。生成的 Taproot 地址具有更高的隐私性、安全性和扩展性。
2.3.BIP86 的优势
- 简洁性:BIP86 提供了一种简洁而直观的路径结构,用于生成 Taproot 地址。
- 安全性:BIP86 使用椭圆曲线算法生成公钥,具有与比特币的现有地址生成方法相当的安全性。
- 可扩展性:BIP86 支持不同的币种和账户,具有良好的可扩展性。
3. BIP44 与 BIP86 的区别与联系
3.1.用途上的区别
- BIP44: 通用的多币种、多账户管理,适用于生成多种类型的地址(P2PKH, P2SH, P2WPKH)。
- BIP86: 专门为生成单密钥 Taproot 地址设计,简化了路径和使用场景。
3.2. 路径结构区别
- BIP44: m/44'/coin_type'/account'/change/address_index,包含多币种、多账户和多地址类型。
- BIP86: m/86'/coin_type'/account'/change/address_index,专注于单密钥 Taproot 地址生成。
3.3.复杂性区别
- BIP44: 适用于更复杂的多币种和多账户需求,路径层次更丰富。
- BIP86: 专注于单一用途(Taproot 地址),路径层次简化。
3.4.标准化区别
- BIP44: 广泛应用于各种钱包和加密货币管理系统,已经成为生成确定性钱包路径的标准。
- BIP86: 主要用于比特币的 Taproot 地址生成,随着 Taproot 的采用而变得更加流行。
3.5.总结
- BIP44 适用于多种类型地址生成,适合更复杂的钱包管理需求。
- BIP86 专注于生成比特币的 Taproot 地址,路径简化,专门为 Taproot 设计。
4.Taproot 为什么需要 BIP86
4.1. 专注于单密钥 Taproot 地址 BIP86 专门设计用于生成单密钥 Taproot 地址。Taproot 是比特币的一项重要升级,旨在提高隐私性、可扩展性和智能合约功能。
4.2.符合新标准和优化 BIP86 引入了一些新的优化和改进,以支持比特币协议的新特性,如 Taproot 和 Schnorr 签名。Taproot 和 Schnorr 签名提供了更高的安全性和更好的隐私特性。BIP86 的设计是为了最大限度地利用这些新特性。
4.3.专注于比特币和未来扩展
BIP44 是一个通用的多币种和多账户路径标准,设计用于管理各种加密货币的地址。这种通用性虽然提供了很大的灵活性,但在特定的优化和新特性支持上可能不如专门设计的标准有效。BIP86 专门为比特币设计,更好地支持比特币的未来扩展和改进。
4.4.简化路径和提高效率
BIP86 路径简化,专注于单一用途,使其更适合于生成和管理比特币的 Taproot 地址。这种简化可以减少实现和使用中的复杂性,提高效率和安全性。
4.5.避免混淆和错误
BIP86 的专用路径减少了使用过程中可能出现的混淆和错误。由于它专门设计用于比特币的 Taproot 地址,开发者和用户不必在多个标准之间切换,减少了出错的可能性。
4.6.代码比较
BIP44 代码示范
def create_address(mnemonic):
seed = bip39.phrase_to_seed(mnemonic)
bip32_root_key = BIP32Key.fromEntropy(seed)
path = "m/44'/0'/0'/0/0"
key = bip32_root_key
for level in path.split('/')[1:]:
if level.endswith("'"):
index = BIP32_HARDEN + int(level[:-1])
else:
index = int(level)
key = key.ChildKey(index)
address = key.Address()
return address
BIP86 代码示例
import bip39
import bech32
from bip32 import BIP32, HARDENED_INDEX
from hashlib import sha256
from ecdsa import SECP256k1, SigningKey
def generate_taproot_address(mnemonic):
seed = bip39.phrase_to_seed(mnemonic)
bip32 = BIP32.from_seed(seed)
path = "m/86'/0'/0'/0/0"
child_key = bip32.get_privkey_from_path(path)
private_key = SigningKey.from_string(child_key, curve=SECP256k1)
public_key = private_key.get_verifying_key().to_string("compressed")
tweak = sha256(b'TapTweak' + public_key[1:]).digest()
tweaked_pubkey = bytearray(public_key)
for i in range(32):
tweaked_pubkey[1 + i] ^= tweak[i]
witver = 1 # witness version for Taproot
witprog = tweaked_pubkey[1:33]
address = bech32.encode('bc', witver, witprog)
return address
5. Taproot 格式地址离线生成
import bip39
import bech32
from bip32 import BIP32, HARDENED_INDEX
from hashlib import sha256
from ecdsa import SECP256k1, SigningKey
def generate_taproot_address(mnemonic):
seed = bip39.phrase_to_seed(mnemonic)
bip32 = BIP32.from_seed(seed)
path = "m/86'/0'/0'/0/0"
child_key = bip32.get_privkey_from_path(path)
private_key = SigningKey.from_string(child_key, curve=SECP256k1)
public_key = private_key.get_verifying_key().to_string("compressed")
tweak = sha256(b'TapTweak' + public_key[1:]).digest()
tweaked_pubkey = bytearray(public_key)
for i in range(32):
tweaked_pubkey[1 + i] ^= tweak[i]
witver = 1 # witness version for Taproot
witprog = tweaked_pubkey[1:33]
address = bech32.encode('bc', witver, witprog)
return address
6. 离线签名
- 构建交易: 创建并填充交易输入和输出。
- 生成交易摘要: 使用交易数据生成 Taproot 交易摘要。
- 签名交易: 使用私钥对交易摘要进行签名。
- 构建完整交易: 将签名附加到交易中并生成最终的交易。
def buildAndsignTx(input_list, output_list, private_key, taproot_pubkey):
tx = Transaction()
for input in input_list:
tx.add_input(input['previous_txid'], input['index'])
for output in output_list:
tx.add_output(output["address"], output["value"])
tx.version = 2
tx.locktime = 0
sighash = tx.signature_hash()
tweaked_privkey = taproot_tweak_seckey(private_key, taproot_pubkey)
signature = schnorr.sign(tweaked_privkey, sighash)
witness = TaprootWitness(taproot_pubkey, signature)
tx.inputs[0].witness = witness
signed_tx = tx.serialize()
return signed_tx