Silent Payments
We capture the simple case of a single payment. We don’t dig into how we can scan the chain to find a transaction with the sender’s public key that can provide the tweak to spend the transaction.
@sender_input_key = key :new (1)
@sender = key :new
@receiver = key even_y: true # Generate a key with even y
extend_chain to: @sender_input_key, num_blocks: 101
@sender_coinbase = spendable_coinbase_for @sender_input_key (2)
@sender_dh_share = multiply point: @receiver, scalar: @sender_input_key (3)
@taproot_output_key = tweak_public_key @receiver, with: hash160(@sender_dh_share) (3)
@taproot_output_tx = transaction inputs: [{ tx: @sender_coinbase,
vout: 0,
script_sig: 'sig:wpkh(@sender_input_key)' }],
outputs: [{ amount: 49.999.sats,
taproot: { internal_key: @taproot_output_key } }]
broadcast @taproot_output_tx
confirm transaction: @taproot_output_tx
extend_chain num_blocks: 100
assert_equal @taproot_output_tx.inputs[0].script_witness.stack[1].bth, @sender_input_key.pubkey
@receiver_dh_share = multiply point: @sender_input_key, scalar: @receiver (4)
@receiver_tweaked_private_key = tweak_private_key @receiver, with: hash160(@receiver_dh_share) (4)
@spend_received_payment_tx = transaction inputs: [{ tx: @taproot_output_tx,
vout: 0,
script_sig: { keypath: @receiver_tweaked_private_key } }], (5)
outputs: [{ amount: 49.998.sats,
descriptor: 'wpkh(@sender)' }]
assert_mempool_accept @spend_received_payment_tx
broadcast @spend_received_payment_tx
confirm transaction: @spend_received_payment_tx
1 | Sender’s input key used in coinbases |
2 | Find a coinbase spendable by sender’s input key |
3 | Generate a tweaked key using receiver’s address and sender’s input key |
4 | Generate a tweaked private key for receiver using the sender’s input key |
5 | Spend the received payment using the receiver’s tweaked key |
We have a jupyter notebook that walks through the above example. Silent Payments Jupyter Notebook