为何Python3无依赖脚本生成特定比特币私钥公钥错误?
问题:脚本生成的私钥对应公钥不符合预期的原因
执行命令btc_address_dump "0x0000000000000000000000000000000000000000000000000000000000000002"时,预期公钥为04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a,但你提供的Python脚本生成的公钥却不一致,核心问题是椭圆曲线点运算(加倍、加法)的公式完全错误,导致私钥乘基点的计算逻辑失效。
具体错误点
1. 点加倍(Double)公式错误
脚本中照搬了复数乘法的逻辑:
Qx = (Qx**2 - Qy**2) % p Qy = (2*Qx*Qy) % p
这完全不符合secp256k1曲线的点加倍规则。针对secp256k1(参数a=0),正确的点加倍步骤为:
- 计算斜率:
λ = (3*x1²) * 模p下(2*y1)的逆元 - 计算新x坐标:
x3 = (λ² - 2*x1) % p - 计算新y坐标:
y3 = (λ*(x1 - x3) - y1) % p
2. 点加法(Add)公式错误
脚本中的加法逻辑同样偏离椭圆曲线规则:
Qx = (Qx*Gx - Qy*Gy) % p Qy = (Qx*Gy + Qy*Gx) % p
正确的点加法(针对两个不同点)步骤为:
- 计算斜率:
λ = (y2 - y1) * 模p下(x2 - x1)的逆元 - 计算新x坐标:
x3 = (λ² - x1 - x2) % p - 计算新y坐标:
y3 = (λ*(x1 - x3) - y1) % p
3. 快速幂循环范围错误
脚本中循环遍历bin(private_key_decimal)[3:],会跳过私钥二进制的前三位(包括有效的最高位1)。比如私钥0x2的二进制是0b10,[3:]会得到空字符串,循环根本不会执行,这也是导致计算结果错误的原因之一。
修正后的脚本
以下是实现了正确椭圆曲线点运算的脚本,生成的公钥会与btc_address_dump的输出一致:
# secp256k1椭圆曲线参数 p = 2**256 - 2**32 - 977 a = 0 b = 7 Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240 Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424 def modinv(x): """计算模p下的逆元,利用费马小定理""" return pow(x, p-2, p) def point_double(x1, y1): """椭圆曲线点加倍操作""" lam = (3 * x1**2 + a) * modinv(2 * y1) % p x3 = (lam**2 - 2 * x1) % p y3 = (lam * (x1 - x3) - y1) % p return (x3, y3) def point_add(x1, y1, x2, y2): """椭圆曲线点加法操作""" if x1 == x2 and y1 == y2: return point_double(x1, y1) lam = (y2 - y1) * modinv(x2 - x1) % p x3 = (lam**2 - x1 - x2) % p y3 = (lam * (x1 - x3) - y1) % p return (x3, y3) def scalar_mult(k, x, y): """标量乘法:计算k倍的基点(x,y)""" result_x, result_y = None, None # 用(None, None)表示无穷远点 current_x, current_y = x, y while k > 0: if k % 2 == 1: if result_x is None: result_x, result_y = current_x, current_y else: result_x, result_y = point_add(result_x, result_y, current_x, current_y) current_x, current_y = point_double(current_x, current_y) k = k // 2 return (result_x, result_y) # 目标私钥 private_key = 0x0000000000000000000000000000000000000000000000000000000000000002 # 计算公钥 Qx, Qy = scalar_mult(private_key, Gx, Gy) # 转换为未压缩公钥格式 public_key_hex = '04' + format(Qx, '064x') + format(Qy, '064x') print("Uncompressed Public Key:", public_key_hex)
内容的提问来源于stack exchange,提问作者Æmmy
相关产品推荐
相关产品推荐

