• About
  • FAQ
  • Earn Bitcoin while Surfing the net
  • Buy & Sell Crypto on Paxful
Newsletter
Approx Foundation
  • Home
    • Home – Layout 1
  • Bitcoin
  • Ethereum
  • Regulation
  • Market
  • Blockchain
  • Business
  • Guide
  • Contact Us
No Result
View All Result
  • Home
    • Home – Layout 1
  • Bitcoin
  • Ethereum
  • Regulation
  • Market
  • Blockchain
  • Business
  • Guide
  • Contact Us
No Result
View All Result
Approx Foundation
No Result
View All Result
Home Bitcoin

python – Why Double SHA-256 Doesn’t Work for Pizza Transaction Input Validation and How to Assemble Correct Signing Data in bitcoin

Moussa by Moussa
January 12, 2025
in Bitcoin
0
peer discovery – how to obtain the IP addresses of nodes for mining pools?
189
SHARES
1.5k
VIEWS
Share on FacebookShare on Twitter


Bitcoin transaction signature validation problems

To validate each input of a Bitcoin transaction using ECDSA on the secp256k1 curve, we need three parameters:

  1. Public key (Uncompressed example: 042e930f39ba62c6534ee98ed20ca98959d34aa9e057cda01cfd422c6bab3667b76426529382c23f42b9b08d7832d4fee1d6b437a8526e59667ce9c4e9dcebcabb)
  2. Signature (Considered in DER format: 30450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e, last byte 01 SIGHASH_ALL removed)
  3. Double hash of raw transaction data that the sender originally signed (Found hash: 083867478cb0d1d8bb864175bbc49728cffcc114bc2e762c6df64f2c965a9a66)

Here’s the documentation on how to create transaction data for signing https://en.bitcoin.it/wiki/OP_CHECKSIG

Everywhere in documentation and books, it is stated how to assemble this data and then compute its double SHA-256 hash

I attempted to gather all the transaction data from this pizza transaction (https://blockstream.info/api/tx/cca7507897abc89628f450e8b1e0c6fca4ec3f7b34cccf55f3f531c659ff4d79) and followed the step-by-step provided in this Bitcoin StackExchange answer

  1. But the author got: 692678553d1b85ccf87d4d4443095f276cdf600f2bb7dd44f6effbd7458fd4c2 after double hashing
  2. And I tried hash it ones, got this: 083867478cb0d1d8bb864175bbc49728cffcc114bc2e762c6df64f2c965a9a66

Used online EDCDSA verification tool https://emn178.github.io/online-tools/ecdsa/verify/

Then look, my input was correct and originally signed message was like this 083867478cb0d1d8bb864175bbc49728cffcc114bc2e762c6df64f2c965a9a66

DOUBLE SHA256:
enter image description here

ONE SHA256:
enter image description here

I wrote a script that takes a transaction ID and validates its inputs, but my script currently returns an entirely incorrect input (public key and signature correct)

Questions:

Here is my code:

import requests
import hashlib
from bitcoin.core import CTransaction, CTxIn, CTxOut, COutPoint, x, b2x, lx
from bitcoin.core.script import CScript
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.exceptions import InvalidSignature
import logging


def fetch_raw_transaction(transaction_id):
    """
    Fetch the raw transaction hex from Blockstream API.

    :param transaction_id: Transaction ID (txid)
    :return: Raw transaction hex
    """
    url = f"https://blockstream.info/api/tx/{transaction_id}/hex"
    response = requests.get(url)
    if response.status_code == 200:
        logging.debug(f"Raw transaction fetched: {response.text.strip()}")
        return bytes.fromhex(response.text.strip())  # Return as bytes
    else:
        raise Exception(f"Failed to fetch transaction. Status code: {response.status_code}")

def fetch_utxo_script_pubkey(txid, vout):
    """
    Fetch the ScriptPubKey for a given UTXO using Blockstream API.

    :param txid: Transaction ID of the UTXO
    :param vout: Output index of the UTXO
    :return: ScriptPubKey as bytes
    """
    url = f"https://blockstream.info/api/tx/{txid}"
    response = requests.get(url)
    if response.status_code == 200:
        outputs = response.json().get('vout', [])

        if 0 <= vout < len(outputs):
            script_pubkey = outputs[vout].get('scriptpubkey', None)
            if script_pubkey:
                return bytes.fromhex(script_pubkey)
            else:
                raise Exception(f"ScriptPubKey not found for vout {vout} in transaction {txid}")
        else:
            raise Exception(f"Invalid vout index: {vout} for transaction {txid}")
    else:
        raise Exception(f"Failed to fetch transaction details. Status code: {response.status_code}")

def compute_sighash(transaction_id, input_index, script_pubkey):
    """
    Compute the sighash for a specific input in a Bitcoin transaction.

    :param transaction_id: Transaction ID (txid)
    :param input_index: Index of the input being signed
    :param script_pubkey: ScriptPubKey for the input
    :return: SIGHASH (message for signature)
    """
    try:
        # Fetch the raw transaction bytes
        raw_tx_bytes = fetch_raw_transaction(transaction_id)
        logging.debug(f"Raw transaction bytes: {raw_tx_bytes.hex()}")

        # Deserialize the transaction
        tx = CTransaction.deserialize(raw_tx_bytes)
        logging.debug(f"Deserialized transaction: {tx}")

        # Create a new transaction with updated scriptSig
        modified_tx_ins = []
        for i, tx_in in enumerate(tx.vin):
            if i == input_index:
                # Replace scriptSig for the specified input
                modified_tx_ins.append(CTxIn(tx_in.prevout, CScript(script_pubkey), tx_in.nSequence))
            else:
                # Keep other inputs unchanged
                modified_tx_ins.append(tx_in)

        modified_tx = CTransaction(modified_tx_ins, tx.vout, tx.nLockTime)
        logging.debug(f"Modified transaction: {modified_tx}")

        # Serialize transaction with SIGHASH_ALL (0x01)
        tx_raw_with_sighash = modified_tx.serialize() + bytes([1])  # 0x01 = SIGHASH_ALL
        logging.debug(f"Serialized transaction with SIGHASH_ALL: {tx_raw_with_sighash.hex()}")

        # Compute double SHA-256
        sighash = hashlib.sha256(hashlib.sha256(tx_raw_with_sighash).digest()).digest()
        logging.debug(f"Computed SIGHASH: {sighash.hex()}")

        return sighash
    except Exception as e:
        logging.error("Error during SIGHASH computation", exc_info=True)
        raise

def verify_signature(public_key_hex, signature_hex, message):
    """
    Verify the signature using ECDSA.

    :param public_key_hex: Hex-encoded public key
    :param signature_hex: Hex-encoded signature
    :param message: Message (hash) to verify
    :return: True if valid, raises exception otherwise
    """
    try:
        public_key_bytes = bytes.fromhex(public_key_hex)
        signature_bytes = bytes.fromhex(signature_hex[:-2])  # Remove the last byte (SIGHASH flag)

        # Load the public key
        public_key = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), public_key_bytes)

        # Verify the signature
        public_key.verify(signature_bytes, message, ec.ECDSA(SHA256()))
        print("Signature is valid.")
    except InvalidSignature:
        print("Signature is invalid.")
    except Exception as e:
        logging.error("Error during signature verification", exc_info=True)
        raise

if __name__ == "__main__":
    transaction_id = "cca7507897abc89628f450e8b1e0c6fca4ec3f7b34cccf55f3f531c659ff4d79"

    try:
        # Fetch the raw transaction bytes
        raw_tx_bytes = fetch_raw_transaction(transaction_id)
        tx = CTransaction.deserialize(raw_tx_bytes)

        # Iterate over each input to validate signatures
        for i, tx_in in enumerate(tx.vin):
            print(f"\nValidating input {i}:")

            # Extract public key and signature from scriptSig
            script_sig = tx_in.scriptSig
            signature_hex = b2x(script_sig[1:1 + script_sig[0]])
            public_key_hex = b2x(script_sig[1 + script_sig[0] + 1:])

            print(f"Public Key: {public_key_hex}")
            print(f"Signature: {signature_hex}")

            # Fetch ScriptPubKey for this input
            prev_txid = b2x(tx_in.prevout.hash[::-1])  # Convert to little-endian
            script_pubkey = fetch_utxo_script_pubkey(prev_txid, tx_in.prevout.n)
            print(f"ScriptPubKey: {script_pubkey.hex()}")

            # Compute sighash
            sighash = compute_sighash(transaction_id, i, script_pubkey)
            print(f"Computed SIGHASH (message): {sighash.hex()}")

            # Verify signature
            verify_signature(public_key_hex, signature_hex, sighash)
    except Exception as e:
        print("Error:", str(e))

Output:

Validating input 0:
Public Key: 042e930f39ba62c6534ee98ed20ca98959d34aa9e057cda01cfd422c6bab3667b76426529382c23f42b9b08d7832d4fee1d6b437a8526e59667ce9c4e9dcebcabb
Signature: 30450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e01
ScriptPubKey: 76a91446af3fb481837fadbb421727f9959c2d32a3682988ac
Computed SIGHASH (message): 48fd60a2aeb4247255d2c1929c28ab47bdaaa49b12ff17e50b742789a3604602
Signature is invalid.

Expected Computed SIGHASH (message) is 083867478cb0d1d8bb864175bbc49728cffcc114bc2e762c6df64f2c965a9a66

Before offering advice, please test the code and verify the correctness of the solution.
I have already reviewed many answers on how to correctly assemble the message for transaction signing, but none of them work

If there is any API that return signed hash of transaction, so I can validate it, please tell me



Source link

Related articles

X Eyes Auto-Lock For Crypto Mentions After Tortoise Death Hoax

X Eyes Auto-Lock For Crypto Mentions After Tortoise Death Hoax

April 3, 2026
XRP Analyst Reveals Why The Altcoin Is Set To Hit $27

XRP Analyst Reveals Why The Altcoin Is Set To Hit $27

April 3, 2026
Share76Tweet47

Related Posts

AI Crypto Predicts the Biggest Plays for April 2026

AI Crypto Predicts the Biggest Plays for April 2026

by Moussa
April 3, 2026
0

While the market continues to range, with US/Israel/Iran tensions keeping retail money away, we will look at what AI crypto...

X Eyes Auto-Lock For Crypto Mentions After Tortoise Death Hoax

X Eyes Auto-Lock For Crypto Mentions After Tortoise Death Hoax

by Moussa
April 3, 2026
0

Trusted Editorial content, reviewed by leading industry experts and seasoned editors. Ad Disclosure A Solana memecoin linked to a false...

XRP Analyst Reveals Why The Altcoin Is Set To Hit $27

XRP Analyst Reveals Why The Altcoin Is Set To Hit $27

by Moussa
April 3, 2026
0

A prominent market analyst has outlined a structured case for XRP reaching as high as $27, arguing that the altcoin’s...

MARA Conducts Ongoing Layoffs Following $1.1B Bitcoin Sale And Debt Reduction Push

MARA Conducts Ongoing Layoffs Following $1.1B Bitcoin Sale And Debt Reduction Push

by Moussa
April 3, 2026
0

Bitcoin miner MARA Holdings has begun a series of company-wide layoffs affecting multiple departments, according to reporting from Blockspace Media,...

Riot Platforms Sells 3,778 Bitcoin in Q1 2026, Raising $289.5 Million for Data Center Expansion – Mining Bitcoin News

Riot Platforms Sells 3,778 Bitcoin in Q1 2026, Raising $289.5 Million for Data Center Expansion – Mining Bitcoin News

by Moussa
April 3, 2026
0

Riot Platforms Q1 2026 Update The Texas-based bitcoin miner and data center developer (Nasdaq: RIOT) sold more than 2.5 times...

Load More

youssufi.com

sephina.com

[vc_row full_width="stretch_row" parallax="content-moving" vc_row_background="" background_repeat="no-repeat" background_position="center center" footer_scheme="dark" css=".vc_custom_1517813231908{padding-top: 60px !important;padding-bottom: 30px !important;background-color: #191818 !important;background-position: center;background-repeat: no-repeat !important;background-size: cover !important;}" footer_widget_title_color="#fcbf46" footer_button_bg="#fcb11e"][vc_column width="1/4"]

We bring you the latest in Crypto News

[/vc_column][vc_column width="1/4"][vc_wp_categories]
[/vc_column][vc_column width="1/4"][vc_wp_tagcloud taxonomy="post_tag"][/vc_column][vc_column width="1/4"]

Newsletter

[vc_raw_html]JTNDcCUzRSUzQ2RpdiUyMGNsYXNzJTNEJTIydG5wJTIwdG5wLXN1YnNjcmlwdGlvbiUyMiUzRSUwQSUzQ2Zvcm0lMjBtZXRob2QlM0QlMjJwb3N0JTIyJTIwYWN0aW9uJTNEJTIyaHR0cHMlM0ElMkYlMkZhcHByb3gub3JnJTJGJTNGbmElM0RzJTIyJTNFJTBBJTBBJTNDaW5wdXQlMjB0eXBlJTNEJTIyaGlkZGVuJTIyJTIwbmFtZSUzRCUyMm5sYW5nJTIyJTIwdmFsdWUlM0QlMjIlMjIlM0UlM0NkaXYlMjBjbGFzcyUzRCUyMnRucC1maWVsZCUyMHRucC1maWVsZC1maXJzdG5hbWUlMjIlM0UlM0NsYWJlbCUyMGZvciUzRCUyMnRucC0xJTIyJTNFRmlyc3QlMjBuYW1lJTIwb3IlMjBmdWxsJTIwbmFtZSUzQyUyRmxhYmVsJTNFJTBBJTNDaW5wdXQlMjBjbGFzcyUzRCUyMnRucC1uYW1lJTIyJTIwdHlwZSUzRCUyMnRleHQlMjIlMjBuYW1lJTNEJTIybm4lMjIlMjBpZCUzRCUyMnRucC0xJTIyJTIwdmFsdWUlM0QlMjIlMjIlM0UlM0MlMkZkaXYlM0UlMEElM0NkaXYlMjBjbGFzcyUzRCUyMnRucC1maWVsZCUyMHRucC1maWVsZC1lbWFpbCUyMiUzRSUzQ2xhYmVsJTIwZm9yJTNEJTIydG5wLTIlMjIlM0VFbWFpbCUzQyUyRmxhYmVsJTNFJTBBJTNDaW5wdXQlMjBjbGFzcyUzRCUyMnRucC1lbWFpbCUyMiUyMHR5cGUlM0QlMjJlbWFpbCUyMiUyMG5hbWUlM0QlMjJuZSUyMiUyMGlkJTNEJTIydG5wLTIlMjIlMjB2YWx1ZSUzRCUyMiUyMiUyMHJlcXVpcmVkJTNFJTNDJTJGZGl2JTNFJTBBJTNDZGl2JTIwY2xhc3MlM0QlMjJ0bnAtZmllbGQlMjB0bnAtcHJpdmFjeS1maWVsZCUyMiUzRSUzQ2xhYmVsJTNFJTNDaW5wdXQlMjB0eXBlJTNEJTIyY2hlY2tib3glMjIlMjBuYW1lJTNEJTIybnklMjIlMjByZXF1aXJlZCUyMGNsYXNzJTNEJTIydG5wLXByaXZhY3klMjIlM0UlQzIlQTBCeSUyMGNvbnRpbnVpbmclMkMlMjB5b3UlMjBhY2NlcHQlMjB0aGUlMjBwcml2YWN5JTIwcG9saWN5JTNDJTJGbGFiZWwlM0UlM0MlMkZkaXYlM0UlM0NkaXYlMjBjbGFzcyUzRCUyMnRucC1maWVsZCUyMHRucC1maWVsZC1idXR0b24lMjIlM0UlM0NpbnB1dCUyMGNsYXNzJTNEJTIydG5wLXN1Ym1pdCUyMiUyMHR5cGUlM0QlMjJzdWJtaXQlMjIlMjB2YWx1ZSUzRCUyMlN1YnNjcmliZSUyMiUyMCUzRSUwQSUzQyUyRmRpdiUzRSUwQSUzQyUyRmZvcm0lM0UlMEElM0MlMkZkaXYlM0UlM0NiciUyRiUzRSUzQyUyRnAlM0U=[/vc_raw_html][/vc_column][/vc_row]
No Result
View All Result
  • Contact Us
  • Homepages
  • Business
  • Guide

© 2024 APPROX FOUNDATION - The Crypto Currency News