You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python从地址生成ECDSA公钥失败及交易验证报错求助

ECDSA签名验证报错:Point does not lay on the curve

我是Python初学者,为提升技术开发了一个钱包包准备集成到其他项目中,但在使用ecdsa库进行交易签名验证时遇到问题,出现如下错误:

Traceback (most recent call last):
File "Programs\Python\Python311\Lib\site-packages\ecdsa\keys.py", line 170, in from_public_point
self.pubkey = ecdsa.Public_key(
^^^^^^^^^^^^^^^^^
File "Programs\Python\Python311\Lib\site-packages\ecdsa\ecdsa.py", line 155, in init
raise InvalidPointError("Point does not lay on the curve")
ecdsa.ecdsa.InvalidPointError: Point does not lay on the curve

处理上述异常时又触发新异常:

Traceback (most recent call last):
File "xxx-wallet\index.py", line 33, in
print("valid tx: ", validate_transaction(_tx, a_unspent_tx_outs))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:neon-wallet\neon_wallet\transaction\transactions.py", line 56, in validate_transaction
[
File "C:neon-wallet\neon_wallet\transaction\transactions.py", line 57, in
validate_tx_in(txIn, transaction, a_unspent_tx_outs)
File "C:neon-wallet\neon_wallet\transaction\transactions.py", line 110, in validate_tx_in
key = ecdsa.VerifyingKey.from_string(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:Programs\Python\Python311\Lib\site-packages\ecdsa\keys.py", line 281, in from_string
return cls.from_public_point(point, curve, hashfunc, validate_point)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Programs\Python\Python311\Lib\site-packages\ecdsa\keys.py", line 174, in from_public_point
raise MalformedPointError("Point does not lay on the curve")
ecdsa.errors.MalformedPointError: Point does not lay on the curve

相关代码如下:

交易验证核心代码

# Define a function that commits a transaction based on unspent
# transaction outputs
def validate_transaction(
    transaction: Transaction, a_unspent_tx_outs: List[UnspentTxOut]
) -> bool:
    "validate transaction"
    # Check if transaction structure is valid
    if not is_valid_transaction_structure(transaction):
        return False

    # Check if transaction id matches
    # to the hash of the transaction contents
    if get_transaction_id(transaction) != transaction.id:
        print("invalid tx id: " + transaction.id)
        return False

    # Check if all transaction inputs are valid
    has_valid_tx_ins = all(
        [
            validate_tx_in(txIn, transaction, a_unspent_tx_outs)
            for txIn in transaction.tx_ins
        ]
    )

    if not has_valid_tx_ins:
        print("some of the txIns are invalid in tx: " + transaction.id)
        return False

    # Calculate sum of amounts of transaction inputs
    t_tx = [get_tx_in_amount(txIn, a_unspent_tx_outs) for txIn in transaction.tx_ins]
    total_tx_in_values = sum(t_tx)

    # Calculate sum of transaction output amounts
    total_tx_out_values = sum([txOut.amount for txOut in transaction.tx_outs])

    # Check if sums are equal
    if total_tx_out_values != total_tx_in_values:
        _id = transaction.id
        print(f"totalTxOutValues !== totalTxInValues in tx: {_id}")
        return False

    return True


# Define a function that validates a transaction input against
# unspent transaction outputs
def validate_tx_in(
    tx_in: TxIn,
    transaction: Transaction,
    a_unspent_tx_outs: List[UnspentTxOut],
) -> bool:
    """validate transaction in"""
    # Find the unspent transaction output that
    # matches transaction input
    referenced_uTx_out = next(
        (
            uTxO
            for uTxO in a_unspent_tx_outs
            if (uTxO.tx_out_id == tx_in.tx_out_id)
            and (uTxO.tx_out_index == tx_in.tx_out_index)
        ),
        None,
    )
    if referenced_uTx_out is None:
        print("referenced txOut not found: " + str(tx_in))
        return False

    # Extract address from unspent transaction output
    address = referenced_uTx_out.address
    print('ref address', address)

    # Create an ECDSA public key from the address
    key = ecdsa.VerifyingKey.from_string(
        bytes.fromhex(address),
        curve=ecdsa.SECP256k1,
    )

    # Check the signature of the transaction entry with the
    # public key and transaction id
    valid_signature = key.verify(
        bytes.fromhex(tx_in.signature), bytes.fromhex(transaction.id)
    )
    if not valid_signature:
        tx_s = tx_in.signature
        tx_id = transaction.id
        ref = referenced_uTx_out.address
        print(f"invalid txIn signature: {tx_s} txId: {tx_id} address: {ref}")
        return False

    return True

测试代码

"""test index"""
from neon_wallet.transaction.transaction import Transaction
from neon_wallet.transaction.transactions import (
    get_transaction_id,
    validate_transaction,
)
from neon_wallet.transaction.tx_in import TxIn
from neon_wallet.transaction.tx_out import TxOut
from tests.transaction.helpers_tests import utxo1, utxo2, utxo3


_tx = Transaction(
    [
        TxIn("tx1", 0, "serges007"),
        TxIn("tx0", 1, "serges007"),
    ],
    [
        TxOut(
            "04b0bd634234abbbcc1ba1e986e884185c61cf43da82f5963a8c70171b1b200c006a8421997ac617d91d406e5fa52bbf4d16e2a069e6b9b668a3935dabaecbc403",
            50.0,
        ),
        TxOut(
            "04b0bd634234abbbdd1ba1e986e884185c61cf43da82f5963a8c70171b1b200c006a8421997ac617d91d406e5fa52bbf4d16e2a069e6b9b668a3935dabaecbc402",
            100.0,
        ),
    ],
)
a_unspent_tx_outs = [utxo1, utxo2, utxo3]
# Appeler la fonction à tester avec cet objet
TX_ID = get_transaction_id(_tx)
# Vérifier que le résultat est conforme à l'attendu
# print("id :", TX_ID)
print("valid tx: ", validate_transaction(_tx, a_unspent_tx_outs))

问题根源与修复方案

1. 核心问题:公钥格式错误

你直接将「钱包地址」(或无效的公钥字符串)传给ecdsa.VerifyingKey.from_string(),但该方法要求输入符合SECP256k1曲线规范的原始公钥字节:

  • 非压缩公钥:65字节,前缀0x04,后接32字节x坐标+32字节y坐标
  • 压缩公钥:33字节,前缀0x02或0x03,后接32字节x坐标

钱包地址是公钥经过SHA256+RIPEMD160哈希再编码得到的,无法直接转成ECDSA验证密钥。

2. 修复步骤

(1)确保UTXO存储的是原始公钥

修改UTXO结构,保存原始公钥的十六进制字符串,而不是钱包地址。如果是测试场景,先生成合法的公钥:

import ecdsa
# 生成合法的SECP256k1密钥对
private_key = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1)
public_key = private_key.get_verifying_key()
# 获取非压缩公钥的十六进制字符串
valid_pub_key_hex = public_key.to_string("uncompressed").hex()
print(valid_pub_key_hex)  # 用这个字符串替换测试代码里的公钥
(2)修正测试签名

测试代码里的"serges007"不是合法的ECDSA签名,需要用对应私钥对交易ID签名:

# 用上面生成的私钥对交易ID签名
tx_id_bytes = bytes.fromhex(TX_ID)
valid_signature_hex = private_key.sign(tx_id_bytes).hex()
# 将测试代码里的TxIn签名替换成这个值
(3)验证公钥合法性(可选)

如果不确定公钥是否合法,可以先做预检查:

pub_key_hex = "你的公钥十六进制字符串"
try:
    pub_key_bytes = bytes.fromhex(pub_key_hex)
    ecdsa.VerifyingKey.from_string(pub_key_bytes, curve=ecdsa.SECP256k1)
    print("公钥合法")
except Exception as e:
    print(f"公钥无效:{e}")
(4)临时绕过检查(仅测试用,禁止生产环境)

如果只是想快速验证逻辑,可添加validate_point=False跳过曲线验证,但这只是临时方案:

key = ecdsa.VerifyingKey.from_string(
    bytes.fromhex(address),
    curve=ecdsa.SECP256k1,
    validate_point=False  # 仅测试用
)

内容的提问来源于stack exchange,提问作者kaito shi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 02:18:07