Python从地址生成ECDSA公钥失败及交易验证报错求助
我是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

