• 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

Cardano News: CIP-0113 Brings Compliance Rules to ADA Tokens

Cardano News: CIP-0113 Brings Compliance Rules to ADA Tokens

October 8, 2026
DSA White Paper Named Among the 2026 DC Fintech Week Winning Papers

BitGo And HashKey Expand Asia Partnership Across Staking, Custody And Tokenization

October 7, 2026
Share76Tweet47

Related Posts

Cardano News: CIP-0113 Brings Compliance Rules to ADA Tokens

Cardano News: CIP-0113 Brings Compliance Rules to ADA Tokens

by Moussa
October 8, 2026
0

In Cardano news today, the Cardano Foundation has launched CIP-0113, a standard that lets issuers build compliance rules into programmable...

DSA White Paper Named Among the 2026 DC Fintech Week Winning Papers

BitGo And HashKey Expand Asia Partnership Across Staking, Custody And Tokenization

by Moussa
October 7, 2026
0

Trusted Editorial content, reviewed by leading industry experts and seasoned editors. Ad Disclosure TL;DR: BitGo and HashKey Cloud have expanded...

Ethereum ETFs Lose $201.9 Million As Bitcoin Funds Return To Inflows

Ethereum ETFs Lose $201.9 Million As Bitcoin Funds Return To Inflows

by Moussa
October 7, 2026
0

Reason to trust Strict editorial policy that focuses on accuracy, relevance, and impartiality Created by industry experts and meticulously reviewed...

U.S. Investors Want To Up Their Crypto Holdings: Charles Schwab

U.S. Investors Want To Up Their Crypto Holdings: Charles Schwab

by Moussa
October 7, 2026
0

Forget stocks — U.S. investors are more interested in upping their bitcoin holdings, according to new research.  Charles Schwab’s 2026...

Quantum-Safe Quantus Launches QTC Token Trading via Near Intents

Quantum-Safe Quantus Launches QTC Token Trading via Near Intents

by Moussa
October 7, 2026
0

Key TakeawaysQuantus launched QTC trading via Near Intents, connecting 180+ assets across more than 30 chains.The model gives new tokens...

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