P2PKH Incomplete code

import hashlib import hmac import requests

Secp256k1 curve parameters

P = 2256 - 232 - 977 N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 A = 0 B = 7 Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240 Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424 SIGHASH_ALL = 0x01

Helper functions

def encode_base58(s, version_byte=b'\x6F'): # Testnet version byte """Encode bytes to Base58Check (used for Bitcoin addresses).""" checksum = hashlib.sha256(hashlib.sha256(version_byte + s).digest()).digest()[:4] num = int.from_bytes(version_byte + s + checksum, 'big') alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" base58 = "" while num: num, remainder = divmod(num, 58) base58 = alphabet[remainder] + base58 # Add leading zeros for each leading zero byte for byte in (version_byte + s): if byte != 0: break base58 = alphabet[0] + base58 return base58

def decode_base58(address, num_bytes=25): base58_chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" base58_map = {char: index for index, char in enumerate(base58_chars)} num = 0 for char in address: num *= 58 if char not in base58_map: raise ValueError(f"Invalid Base58 character: {char}") num += base58_map[char] combined = num.to_bytes(num_bytes, byteorder="big") checksum = combined[-4:] payload = combined[:-4] if hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4] != checksum: raise ValueError("Invalid checksum") return payload[1:] # Remove version byte

def hash160(data): return hashlib.new('ripemd160', hashlib.sha256(data).digest()).digest()

def hash256(s): return hashlib.sha256(hashlib.sha256(s).digest()).digest()

def encode_varint(n): if n < 253: return n.to_bytes(1, 'little') elif n < 65536: return b'\xfd' + n.to_bytes(2, 'little') elif n < 4294967296: return b'\xfe' + n.to_bytes(4, 'little') else: return b'\xff' + n.to_bytes(8, 'little')

def deterministic_k(secret, z): k = b'\x00' * 32 v = b'\x01' * 32 if z > N: z -= N z_bytes = z.to_bytes(32, 'big') secret_bytes = secret.to_bytes(32, 'big') k = hmac.new(k, v + b'\x00' + secret_bytes + z_bytes, hashlib.sha256).digest() v = hmac.new(k, v, hashlib.sha256).digest() k = hmac.new(k, v + b'\x01' + secret_bytes + z_bytes, hashlib.sha256).digest() v = hmac.new(k, v, hashlib.sha256).digest() while True: v = hmac.new(k, v, hashlib.sha256).digest() candidate = int.from_bytes(v, 'big') if 1 <= candidate < N: return candidate k = hmac.new(k, v + b'\x00', hashlib.sha256).digest() v = hmac.new(k, v, hashlib.sha256).digest()

ECC Classes

class Point: def init(self, x, y, a, b): self.x = x self.y = y self.a = a self.b = b if x is not None and y is not None: if (y * y - (x * x * x + a * x + b)) % P != 0: raise ValueError("Point is not on the curve")

def __add__(self, other):
    if self.x is None:
        return other
    if other.x is None:
        return self
    if self.x == other.x and self.y != other.y:
        return self.__class__(None, None, self.a, self.b)
    if self.x == other.x and self.y == other.y:
        if self.y == 0:
            return self.__class__(None, None, self.a, self.b)
        s = (3 * self.x * self.x + self.a) * pow(2 * self.y, P - 2, P) % P
    else:
        s = (other.y - self.y) * pow(other.x - self.x, P - 2, P) % P
    x = (s * s - self.x - other.x) % P
    y = (s * (self.x - x) - self.y) % P
    return self.__class__(x, y, self.a, self.b)

def __rmul__(self, coefficient):
    coef = coefficient % N
    current = self
    result = self.__class__(None, None, self.a, self.b)
    while coef:
        if coef & 1:
            result = result + current
        current = current + current
        coef >>= 1
    return result

def sec(self, compressed=True):
    if self.x is None:
        raise ValueError("Cannot serialize point at infinity")
    if compressed:
        prefix = b'\x02' if self.y % 2 == 0 else b'\x03'
        return prefix + self.x.to_bytes(32, 'big')
    else:
        return b'\x04' + self.x.to_bytes(32, 'big') + self.y.to_bytes(32, 'big')

G = Point(Gx, Gy, A, B)

class PrivateKey: def init(self, secret): self.secret = secret self.point = secret * G

def sign(self, z):
    k = deterministic_k(self.secret, z)
    r = (k * G).x % N
    k_inv = pow(k, N - 2, N)
    s = (z + r * self.secret) * k_inv % N
    if s > N / 2:
        s = N - s
    return Signature(r, s)

class Signature: def init(self, r, s): self.r = r self.s = s

def der(self):
    r_bin = self.r.to_bytes(32, "big").lstrip(b"\x00")
    if r_bin[0] & 0x80:
        r_bin = b"\x00" + r_bin
    s_bin = self.s.to_bytes(32, "big").lstrip(b"\x00")
    if s_bin[0] & 0x80:
        s_bin = b"\x00" + s_bin
    result = bytes([0x02, len(r_bin)]) + r_bin + bytes([0x02, len(s_bin)]) + s_bin
    return bytes([0x30, len(result)]) + result

class Script: def init(self, commands): self.commands = commands

def serialize(self):
    result = b""
    for cmd in self.commands:
        if isinstance(cmd, int):
            result += bytes([cmd])
        elif isinstance(cmd, bytes):
            data_len = len(cmd)
            if data_len <= 75:
                result += bytes([data_len]) + cmd
            elif data_len <= 255:
                result += bytes([0x4c]) + bytes([data_len]) + cmd
            elif data_len <= 65535:
                result += bytes([0x4d]) + data_len.to_bytes(2, 'little') + cmd
            else:
                result += bytes([0x4e]) + data_len.to_bytes(4, 'little') + cmd
    return result

def p2pkh_script(hash160): return Script([0x76, 0xa9, hash160, 0x88, 0xac])

class TxIn: def init(self, prev_tx, prev_index): self.prev_tx = prev_tx self.prev_index = prev_index self.script_sig = Script([]) self.sequence = b"\xff\xff\xff\xff"

class TxOut: def init(self, amount, script_pubkey): self.amount = amount self.script_pubkey = script_pubkey

class Tx: def init(self, version, tx_ins, tx_outs, locktime, testnet=False): self.version = version self.tx_ins = tx_ins self.tx_outs = tx_outs self.locktime = locktime self.testnet = testnet

def sig_hash(self, index, prev_script_pubkey):
    result = self.version.to_bytes(4, "little")
    result += encode_varint(len(self.tx_ins))
    for i, tx_in in enumerate(self.tx_ins):
        result += tx_in.prev_tx[::-1]
        result += tx_in.prev_index.to_bytes(4, "little")
        if i == index:
            serialized_script = prev_script_pubkey.serialize()
            result += encode_varint(len(serialized_script))
            result += serialized_script
        else:
            result += b"\x00"
        result += tx_in.sequence
    result += encode_varint(len(self.tx_outs))
    for tx_out in self.tx_outs:
        result += tx_out.amount.to_bytes(8, "little")
        serialized_script = tx_out.script_pubkey.serialize()
        result += encode_varint(len(serialized_script))
        result += serialized_script
    result += self.locktime.to_bytes(4, "little")
    result += SIGHASH_ALL.to_bytes(4, "little")
    return int.from_bytes(hash256(result), "big")

def serialize(self):
    result = self.version.to_bytes(4, "little")
    result += encode_varint(len(self.tx_ins))
    for tx_in in self.tx_ins:
        result += tx_in.prev_tx[::-1]
        result += tx_in.prev_index.to_bytes(4, "little")
        serialized_script = tx_in.script_sig.serialize()
        result += encode_varint(len(serialized_script))
        result += serialized_script
        result += tx_in.sequence
    result += encode_varint(len(self.tx_outs))
    for tx_out in self.tx_outs:
        result += tx_out.amount.to_bytes(8, "little")
        serialized_script = tx_out.script_pubkey.serialize()
        result += encode_varint(len(serialized_script))
        result += serialized_script
    result += self.locktime.to_bytes(4, "little")
    return result

Main script

if name == "main": # Step 1: Define the Input prev_tx = bytes.fromhex("27e97f3486ef5f837503d53aeeab6577b606e5bf71895fef4453702c4fc7bcea") prev_index = 1 tx_in = TxIn(prev_tx, prev_index)

# Step 2: Calculate Amounts
fee = 2000
utxo_amount = 59999
target_amount = utxo_amount - fee  # 112541 satoshis
print(f"Target amount to send: {target_amount} satoshis")

# Step 3: Define the Single Target Output
target_address = "mu6Hgf5PNLxq4y3dBZt8WyXt4fKG5JrKGj"
target_h160 = decode_base58(target_address)
target_script = p2pkh_script(target_h160)
target_output = TxOut(amount=target_amount, script_pubkey=target_script)

# Step 4: Create the Transaction with One Output
tx_obj = Tx(version=1, tx_ins=[tx_in], tx_outs=[target_output], locktime=0, testnet=True)

# Step 5: Derive Private Key and Public Key
passphrase = b"652025"
secret = int.from_bytes(hash256(passphrase), 'big')
private_key = PrivateKey(secret=secret)
sec = private_key.point.sec(compressed=True)

# Step 6: Generate UTXO Address from Private Key
pubkey_hash160 = hash160(sec)
utxo_address = encode_base58(pubkey_hash160, version_byte=b'\x6F')  # Testnet
print(f"Generated UTXO address: {utxo_address}")
utxo_h160 = pubkey_hash160  # Use directly, no need to decode
prev_script_pubkey = p2pkh_script(utxo_h160)

# Step 7: Sign the Transaction
z = tx_obj.sig_hash(0, prev_script_pubkey)
print(f"Computed sig_hash: {z}")
signature = private_key.sign(z)
der = signature.der()
print(f"DER Signature: {der.hex()}")
sig = der + SIGHASH_ALL.to_bytes(1, "big")

# Step 8: Verify Public Key Matches UTXO
print(f"Public key hash160: {pubkey_hash160.hex()}")
print(f"UTXO hash160: {utxo_h160.hex()}")
print(f"Matches UTXO hash160: {pubkey_hash160 == utxo_h160}")  # Should always be True
if not pubkey_hash160 == utxo_h160:
    print("Error: Generated public key does not match UTXO address!")
    exit(1)

# Step 9: Construct scriptSig
script_sig = Script([sig, sec])
tx_obj.tx_ins[0].script_sig = script_sig
print(f"ScriptSig: {script_sig.serialize().hex()}")

# Step 10: Serialize the Transaction
serialized_tx = tx_obj.serialize().hex()
print(f"Serialized Transaction: {serialized_tx}")

# Step 11: Broadcast the Transaction
url = "https://api.blockchair.com/bitcoin/testnet/push/transaction"
headers = {'Content-Type': 'application/json'}
data = {"tx": serialized_tx}
response = requests.post(url, json=data, headers=headers)
if response.status_code == 200:
    print("Transaction successfully broadcasted.")
    print(f"Transaction ID: {response.json().get('data', {}).get('transaction_hash')}")
else:
    print(f"Failed to broadcast transaction (Blockchair): {response.text}")
    url = "https://blockstream.info/testnet/api/tx"
    response = requests.post(url, data=serialized_tx)
    if response.status_code == 200:
        print("Transaction successfully broadcasted (Blockstream).")
        print(f"Transaction ID: {response.text}")
    else:
        print(f"Failed to broadcast transaction (Blockstream): {response.text}").     I successfully broadcasted this transaction but my code is not fully completed(like combined command set,push opcodes etc.


from Recent Questions - Bitcoin Stack Exchange https://ift.tt/cP2d5le
via IFTTT

Popular posts from this blog

Bitcoin Mining Could Be Strengthening The Ruble, Russian Central Bank Says

Crypto Exec Warns Tokenization Is Moving Faster Than Expected

Bitwise Clients Pour $69M Into Solana as Bulls Fight to Reclaim $200 Resistance Zone