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