Hi there!
So I've been researching about nonces lately, and found an address that reused many nonces across its transactions. The address is empty obviously; however, the Python code generated by Sonnet 3.7 doesn't seem to give the correct one, and I am not super sure what's the issue. Here's the code for instance:
import hashlib
import binascii
import base58
from ecdsa import SigningKey, SECP256k1, util
def extract_r_s_from_signature(signature):
# Remove the [ALL] suffix if present
signature = signature.split('[ALL]')[0]
# The signature is in DER format
# Extract the raw hex string
hex_str = signature
# Skip the first 4 bytes (30 + length + 02 + r_length) to get to r
r_start = 8 # Skip '3044' and '0220'
r_end = r_start + 64 # r is 32 bytes (64 hex chars)
r_hex = hex_str[r_start:r_end]
# After r, there's another 02 marker and length byte for s
s_start = r_end + 4 # Skip '02' and '20' (or '21' if s has a leading zero)
s_hex = hex_str[s_start:]
return int(r_hex, 16), int(s_hex, 16)
def calculate_private_key(z1, z2, s1, s2, r):
# Calculate private key from two different signatures with the same r value
s1_minus_s2 = (s1 - s2) % SECP256k1.order
z1_minus_z2 = (z1 - z2) % SECP256k1.order
# Calculate the modular inverse
s1_minus_s2_inv = pow(s1_minus_s2, -1, SECP256k1.order)
# Calculate private key
private_key = (z1_minus_z2 * s1_minus_s2_inv) % SECP256k1.order
return private_key
def calculate_nonce(z, s, r, private_key):
# Calculate the nonce (k) used in the signature
r_inv = pow(r, -1, SECP256k1.order)
nonce = (r_inv * ((s * private_key) - z)) % SECP256k1.order
return nonce
def private_key_to_wif(private_key_hex, compressed=True):
# Add version byte (0x80 for mainnet)
extended_key = "80" + private_key_hex
# Add compression flag if needed
if compressed:
extended_key += "01"
# First round of SHA-256
first_sha = hashlib.sha256(binascii.unhexlify(extended_key)).digest()
# Second round of SHA-256
second_sha = hashlib.sha256(first_sha).digest()
# First 4 bytes of the second SHA-256 hash are the checksum
checksum = binascii.hexlify(second_sha[:4]).decode()
# Add the checksum to the extended key
wif_key = extended_key + checksum
# Convert to base58
wif = base58.b58encode(binascii.unhexlify(wif_key)).decode()
return wif
def verify_private_key(private_key_hex, public_key_hex):
# Convert private key to SigningKey
private_key_bytes = bytes.fromhex(private_key_hex)
sk = SigningKey.from_string(private_key_bytes, curve=SECP256k1)
# Get public key from private key
vk = sk.get_verifying_key()
# Adjust for compressed public key format
computed_x = vk.pubkey.point.x()
computed_y = vk.pubkey.point.y()
# Check if y is even or odd for the compressed format prefix
prefix = '02' if computed_y % 2 == 0 else '03'
computed_compressed = prefix + hex(computed_x)[2:].zfill(64)
return computed_compressed == public_key_hex
def main():
# Data from the first signature
address = "1BTrViTDXhWrdw5ErBWSyP5LdzYmeuDTr2"
public_key = "03c88e78a3f105d99b7b0643f3cfca56bad5ffd2c8e1bc055d8c6d51475bc6b2cf"
tx_hash1 = "223d80bffcb8cc519f23d6e7795693c5c0b25a1f3c477a96632f875c067d2439"
r_value = "0c9a907263e472822c3afc1df2f87c95b9c8f9956ab891a3f7b3f482fc16814d"
sig1 = "304402200c9a907263e472822c3afc1df2f87c95b9c8f9956ab891a3f7b3f482fc16814d022055dde0ae98f2ffad66a888c3ea22d8de0635062b65ff06b75191e4085035fa61"
# Data from the second signature
tx_hash2 = "8b044016b8307dd8aefe5dcb61cfac97c01122f578fc7e4192472c45405e0a74"
sig2 = "304402200c9a907263e472822c3afc1df2f87c95b9c8f9956ab891a3f7b3f482fc16814d022061f915743d2dadd8856c53405a4fb8e1e0ee974a852dbc2c89649e790dd157ac"
# Print the actual R-value for debugging
print(f"Expected R-value: {r_value}")
# Directly use the R-value from the input
r = int(r_value, 16)
# Extract S values from signatures - we'll only extract S since we know R
_, s1 = extract_r_s_from_signature(sig1)
_, s2 = extract_r_s_from_signature(sig2)
print(f"S1: {hex(s1)[2:]}")
print(f"S2: {hex(s2)[2:]}")
# Convert transaction hashes to integers
z1 = int(tx_hash1, 16)
z2 = int(tx_hash2, 16)
# Calculate the private key
private_key = calculate_private_key(z1, z2, s1, s2, r)
private_key_hex = hex(private_key)[2:].zfill(64)
# Generate compressed WIF format private key (for Electrum)
wif_compressed = private_key_to_wif(private_key_hex, compressed=True)
# Calculate the nonce used
nonce = calculate_nonce(z1, s1, r, private_key)
nonce_hex = hex(nonce)[2:].zfill(64)
# Verify if the calculated private key corresponds to the public key
is_valid = verify_private_key(private_key_hex, public_key)
print(f"Found private key (hex): {private_key_hex}")
print(f"Compressed WIF for Electrum: {wif_compressed}")
print(f"Nonce (k) used: {nonce_hex}")
print(f"Private key verification: {'Successful' if is_valid else 'Failed'}")
# Print instructions for using in Electrum
print("\n=== ELECTRUM IMPORT INSTRUCTIONS ===")
print("1. Open Electrum wallet")
print("2. Go to Wallet -> Private Keys -> Import")
print("3. Paste the Compressed WIF key")
print("4. Electrum will scan for transactions and show any balance associated with this key")
print("5. Transfer any funds to a new, secure wallet immediately")
if __name__ == "__main__":
main()
Address involved: 1BTrViTDXhWrdw5ErBWSyP5LdzYmeuDTr2
Transaction 1 I chose: 223d80bffcb8cc519f23d6e7795693c5c0b25a1f3c477a96632f875c067d2439
Transaction 2 I chose: 8b044016b8307dd8aefe5dcb61cfac97c01122f578fc7e4192472c45405e0a74
What I am doing wrong?