import hashlib
import hmac
import requests
# Secp256k1 curve parameters
P = 2**256 - 2**32 - 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.











