def BIP_143_raw_transaction(prev_tx_id: str, amount_to_be_sent: int | float, signatureScript: str | None, pubKeyScript: str | None):
# Version
version = bytes.fromhex("02000000")
# HashPrevOuts = prev_tx + vout
HashPrev_out = bytes.fromhex(double_sha256(
"0"*72))
# HashSequence
HashSequence = bytes.fromhex(double_sha256("f"*8))
# HashOutputs for ALL signHash
HashOutputs = bytes.fromhex(double_sha256(
"a08601000000000017a914043f512301b66ffa8d73e71907e2b0b80989521587"))
# Hash preimage for all
raw_hash_pre_images = bytearray()
raw_hash_pre_images.extend(version)
raw_hash_pre_images.extend(HashPrev_out)
raw_hash_pre_images.extend(HashSequence)
raw_hash_pre_images.extend(bytes.fromhex(
"000000000000000000000000000000000000000000000000000000000000000000000000"))
# CORRECTION 2 scriptcode
#ONLY ERROR IF THIS
raw_hash_pre_images.extend(bytes.fromhex(
"475221032ff8c5df0bc00fe1ac2319c3b8070d6d1e04cfbf4fedda499ae7b775185ad53b21039bbc8d24f89e5bc44c5b0d1980d6658316a6b2440023117c3c03a4975b04dd5652ae"))
raw_hash_pre_images.extend(bytes.fromhex("a086010000000000"))
raw_hash_pre_images.extend(bytes.fromhex("ffffffff"))
raw_hash_pre_images.extend(HashOutputs)
raw_hash_pre_images.extend(bytes.fromhex("00000000"))
raw_hash_pre_images.extend(bytes.fromhex("01000000"))
return raw_hash_pre_images.hex()
def double_sha256(hex_string):
binary_data = binascii.unhexlify(hex_string)
# return hashlib.sha256(hashlib.sha256(binary_data).digest()).digest()[::-1].hex()
return hashlib.sha256(hashlib.sha256(binary_data).digest()).hexdigest()
def finalize_signed_transaction(raw_tx, signatures, redeem_script, signatureScript: str, prev_tx_id: str, amount_to_be_sent: int, pubKeyScript: str):
try:
witness_stack = bytearray()
# WITNESS STACK SIZE
witness_stack.extend(bytes.fromhex("04"))
witness_stack.extend(bytes.fromhex("00"))
for sig in signatures:
witness_stack.extend(struct.pack("<B", len(bytes.fromhex(sig))))
witness_stack.extend(bytes.fromhex(sig))
witness_stack.extend(struct.pack(
"<B", len(bytes.fromhex(redeem_script))))
witness_stack.extend(bytes.fromhex(redeem_script))
version = bytes.fromhex("02000000")
marker = bytes.fromhex("00")
flag = bytes.fromhex("01")
inputs = bytes.fromhex("01")
# txid
inputTx = bytes.fromhex(prev_tx_id)
vout_index = bytes.fromhex("00000000")
# signatureScript length <OP><256HashofRedeemScript/witnessScript>
sigScriptlen = struct.pack('<B', len(bytes.fromhex(signatureScript)))
# print(sigScriptlen.hex(),"LENGTH")
sequence = bytes.fromhex("ffffffff")
# Outputs
numOutputs = bytes.fromhex("01")
# satoshis
satoshi_to_be_transferred = bytes.fromhex(
struct.pack('<Q', amount_to_be_sent).hex())
locktime = bytes.fromhex("00000000")
raw_final_tx = bytearray()
raw_final_tx.extend(version)
raw_final_tx.extend(marker)
raw_final_tx.extend(flag)
raw_final_tx.extend(inputs)
raw_final_tx.extend(inputTx)
raw_final_tx.extend(vout_index)
raw_final_tx.extend(sigScriptlen)
raw_final_tx.extend(bytes.fromhex(signatureScript))
raw_final_tx.extend(sequence)
raw_final_tx.extend(numOutputs)
raw_final_tx.extend(satoshi_to_be_transferred)
raw_final_tx.extend(struct.pack(
'<B', len(bytes.fromhex(pubKeyScript))))
raw_final_tx.extend(bytes.fromhex(pubKeyScript))
raw_final_tx.extend(witness_stack)
raw_final_tx.extend(locktime)
return raw_final_tx.hex()
except Exception as e:
print("Error finalizing transaction:", e)
return None
def signScript_P2SH_P2WSH(witness_script_hash: str):
# CHANGE NUMBER 1
signScript = bytearray()
signScript.extend(bytes.fromhex("22"))
signScript.extend(bytes.fromhex("00"))
signScript.extend(struct.pack(
"<B", len(bytes.fromhex(witness_script_hash))))
signScript.extend(bytes.fromhex(witness_script_hash))
# SOMEHOW CSCRIPT IS NOT TAKING THE LENGTH ITSELF CAUSING AN ERROR WAY TOO MUCH TIME TO DEBUG THIS
# sigScript = CScript([
# OP_0,
# bytes.fromhex(witness_script_hash)
# ])
return signScript.hex()
def signRawtransaction_P2SH_P2WSH(transaction_hash: str, private_key_arr: list[str]):
signatures_arr = []
public_key_arr = []
for private_key in private_key_arr:
sk = ecdsa.SigningKey.from_string(
bytes.fromhex(private_key), curve=ecdsa.SECP256k1)
x = deterministic_nonce_generator(custom_nonce)
sig_der = sk.sign_digest_deterministic(
bytes.fromhex(transaction_hash),
sigencode=ecdsa.util.sigencode_der,
)
sig_der_with_sighash = sig_der + b'\x01'
signatures_arr.append(sig_der_with_sighash.hex())
return (signatures_arr, public_key_arr)
# It will generate the PubkeyScript for the P2SH-P2WSH Output following the corresponding format
# <OP><Script_hash><OP>
def P2SH_P2WSH_PubKeyScript(redeem_script_hash: str):
try:
pubKeyScript = CScript([
OP_HASH160,
bytes.fromhex(redeem_script_hash),
OP_EQUAL
])
return pubKeyScript.hex()
except Exception as error:
print("An error ocurred while generating the PubKey Script for P2SH-P2WSH Multi-sig transaction - :", error)
def util_main():
# IT WORKS
private_key_arr = ["39dc0a9f0b185a2ee56349691f34716e6e0cda06a7f9707742ac113c4e2317bf",
"5077ccd9c558b7d04a81920d38aa11b4a9f9de3b23fab45c3ef28039920fdd6d"]
# SHA 256 on redeem Script
hash_redeem_P2WSH = hashlib.sha256(bytes.fromhex(
"5221032ff8c5df0bc00fe1ac2319c3b8070d6d1e04cfbf4fedda499ae7b775185ad53b21039bbc8d24f89e5bc44c5b0d1980d6658316a6b2440023117c3c03a4975b04dd5652ae")).hexdigest()
print(hash_redeem_P2WSH, " HASHING THE REDEEM SCRIPT")
# Witness Program
scr = CScript([
OP_0,
bytes.fromhex(hash_redeem_P2WSH)
])
# Hashing the Witness program
scr_hash = hash_redeem_script(bytes.fromhex(scr.hex()))
print(scr_hash, " HASHING THE WITNESS PROGRAM")
# Recepient Address from the Script Hash
scr_add = generate_token_address(scr_hash)
print(scr_add, " Address after checksum and base58 decode")
# Generating the output lock script or the pubKeyScript
pub_key_P2SH_P2WSH = P2SH_P2WSH_PubKeyScript(scr_hash)
print(pub_key_P2SH_P2WSH, " PubKeyScript Hex for P2SH P2WSH transaction")
# Generating raw unsigned transaction for P2SH_P2WSH as The signScript will remain empty in case of witness program
raw_tx_P2SH_P2WSH = createRawTransaction(
"0000000000000000000000000000000000000000000000000000000000000000", 100000, "", pub_key_P2SH_P2WSH)
print(raw_tx_P2SH_P2WSH, " Raw unsigned transaction for P2SH P2WSH")
# breakpoint()
bip_143_raw = BIP_143_raw_transaction("", 1, "", P2SH_P2WSH_PubKeyScript)
print(bip_143_raw, " Raw BIP 143 raw transaction for ALL SigHash")
# CORRECTION 3
# s256 = hashlib.sha256(hashlib.sha256(
# bytes.fromhex(bip_143_raw)).digest()).digest()
# s256 = hashlib.sha256(bytes.fromhex(bip_143_raw)).hexdigest()
s256 = double_sha256(bip_143_raw)
print(s256, " Hex for BIP - 143")
signatures = signRawtransaction_P2SH_P2WSH(s256, private_key_arr)
print(signatures[0], " Signatures from raw BIP-143")
sigScript = signScript_P2SH_P2WSH(hash_redeem_P2WSH)
print(sigScript, " Signature Script for P2SH_P2WSH")
signed_transaction = finalize_signed_transaction(
raw_tx_P2SH_P2WSH, signatures[0], "5221032ff8c5df0bc00fe1ac2319c3b8070d6d1e04cfbf4fedda499ae7b775185ad53b21039bbc8d24f89e5bc44c5b0d1980d6658316a6b2440023117c3c03a4975b04dd5652ae", sigScript, "0000000000000000000000000000000000000000000000000000000000000000", 100000, pub_key_P2SH_P2WSH)
print(signed_transaction)
# signature script is 256sha hash of witness script
return signed_transaction
I don’t understand but the signatures are somehow coming out same each time ? I have checked multiple times at different sources but nothing answers it
I am trying to construct a P2SH-P2WSH transaction from scratch can someone help me debug the above implementation and what is wrong with it?