TeachMeBitcoin

Pay-to-Contract Scripts - Advanced Pattern

From TeachMeBitcoin, the free encyclopedia Reading time: 2 min

15. Pay-to-Contract Scripts — Advanced Pattern

Overview

Pay-to-Contract (P2C) is a cryptographic technique — not a distinct on-chain script type — where the recipient's public key is tweaked to commit to a contract or message, creating a provably linked commitment. The sender tweaks the recipient's key with a hash of the contract terms; the recipient can always derive the corresponding private key and prove the payment was intended for that specific contract.

Mathematical Foundation

Standard key pair: (d, P) where P = d·G

P2C key tweaking:
  t = hash(P || contract_data)   <- tweak scalar
  P' = P + t·G                   <- tweaked public key (payment address)
  d' = d + t                     <- corresponding private key (known only to recipient)

Proof of commitment:
  Given (P, P', contract_data):
  Verify: P' - P == hash(P || contract_data)·G
  This proves P' commits to contract_data relative to key P

On-Chain Implementation

The payment is made to a standard script (P2WPKH, P2TR) using the tweaked key P':

def pay_to_contract(recipient_pubkey: bytes, contract_data: bytes) -> bytes:
    """
    Returns a P2WPKH locking script committing to contract_data.
    """
    # Compute tweak
    tweak_input = recipient_pubkey + contract_data
    t = int.from_bytes(sha256(tweak_input), 'big') % SECP256K1_ORDER
    # Tweaked public key
    P = decode_pubkey(recipient_pubkey)
    t_G = ec_multiply(t, GENERATOR)
    P_prime = ec_add(P, t_G)
    tweaked_pubkey = encode_pubkey(P_prime)
    # Build P2WPKH script
    keyhash = hash160(tweaked_pubkey)
    return bytes([0x00, 0x14]) + keyhash

Use Cases


1. Invoice commitment: Payment proves agreement to specific invoice terms

2. Document notarization: Payment to tweaked key proves document existed at block time

3. Statechains: Key transfer without on-chain transaction

4. BOLT 11/12 (Lightning): Payment secrets and payment metadata embedding

5. Taproot base: Taproot's key tweaking mechanism IS pay-to-contract

Relationship to Taproot

Taproot's output key is literally a P2C commitment to the Tapscript tree:

Internal key: P
Script tree Merkle root: R
Taproot tweak: t = tagged_hash("TapTweak", bytes(P) || R)
Output key: Q = P + t·G

The output key Q commits to both:
  - The internal key P (for key path spending)
  - All scripts in the Merkle tree (for script path spending)
☕ Help support TeachMeBitcoin

TeachMeBitcoin is an ad-free, open-source educational repository curated by a passionate team of Bitcoin researchers and educators for public benefit. If you found our articles helpful, please consider supporting our hosting and ongoing content updates with a clean donation:

Ethereum: 0x578417C51783663D8A6A811B3544E1f779D39A85
Bitcoin: bc1q77k9e95rn669kpzyjr8ke9w95zhk7pa5s63qzz
Solana: 4ycT2ayqeMucixj3wS8Ay8Tq9NRDYRPKYbj3UGESyQ4J
Address copied to clipboard!