Blockchain

Why Every Input in a Bitcoin Transaction Needs Its Own Signature

Understand the necessity of multi-input signatures in Bitcoin transactions, avoid common misunderstandings, and learn the basics of P2PKH signing, SDK usage, and what signatures actually protect.

Ethan Lin

Ethan Lin

technical_editor

Published Jun 18, 20264 min read

In a Nutshell

Signing each input means providing the unlocking proof that satisfies the locking script for every UTXO being spent in a transaction. A transaction can have multiple inputs, each spending a different previous output, so every input must independently prove the right to spend its corresponding UTXO.

Why Every Input in a Bitcoin Transaction Needs Its Own Signature article cover

Why Not Just Sign Once?

Newcomers often assume a single signature covers an entire transaction, but the reality depends on the number of inputs. If a transaction has three inputs:

TEXT
1input 0 -> spends UTXO A
2input 1 -> spends UTXO B
3input 2 -> spends UTXO C

Then each input must satisfy the locking conditions of the UTXO it references. These UTXOs may belong to the same private key, different private keys, or even use different script types. A signature is not a simple "stamp" on the whole transaction—each input needs to complete its own unlocking logic.

The P2PKH Signing Logic

The locking script of a standard P2PKH output looks roughly like this:

You must provide:

  1. A public key.
  2. A signature.

And:

  1. The public key hash must equal the hash locked in the output.
  2. The signature must be verifiable by that public key.

When spending this output, the input's unlocking script typically contains <signature> <public key>. During validation, the unlocking script and the previous output's locking script are combined and evaluated; if the result is true, that input is considered valid.

Creating a Signature Template with the SDK

In the official low-level examples, a P2PKH input uses unlockingScriptTemplate: new P2PKH().unlock(privateKey). A complete snippet looks like:

TypeScript
1tx.addInput({
2 sourceTransaction: Transaction.fromHex(sourceTxHex),
3 sourceOutputIndex: 0,
4 unlockingScriptTemplate: new P2PKH().unlock(privateKey)
5})

This step does not immediately produce the final signature; instead, it tells the SDK that this input should be unlocked using the P2PKH template with the given private key. Later, when you call await tx.sign(), the SDK generates the corresponding unlocking scripts for the inputs that need signing.

What Does the Signature Protect?

The purpose of the signature is not just to prove "I know a private key." It also binds critical parts of the current transaction together to prevent anyone from modifying the transaction and reusing your signature. For example, if the signature didn't cover the outputs, an attacker could change the recipient address to their own; if it didn't cover the amount or script context, validation could be bypassed. Bitcoin transaction signatures follow specific sighash rules—while you don't need to memorize all the details at the beginner stage, you should know that the signature is tied to the current transaction structure and cannot be understood in isolation from the transaction context.

Signing Order for Multi-Input Transactions

When constructing a multi-input transaction, the typical sequence is:

  1. Select inputs
  2. Create outputs
  3. Add change
  4. Calculate the fee
  5. Sign each input
  6. Verify the transaction
  7. Serialize or broadcast

If you modify outputs after signing, the signatures may become invalid because signatures usually commit to the transaction's content. Do not treat signing as a step after which you can freely alter the transaction.

Wallet Signatures vs. Low-Level Private Key Signatures

In the BSV tech stack, Stage 7 uses WalletClient, where the wallet manages signatures and the application never directly touches private keys. Stage 8 explores low-level signing to understand the mechanism. Real user applications should still prefer the wallet interface, keeping private keys inside the wallet.

The differences:

  • WalletClient signing: The private key stays in the wallet; the application requests the wallet to create or sign actions. This is suitable for user-facing apps.
  • Low-level private key signing: The code directly holds the private key, and the developer manually constructs and signs transactions. This is suitable for learning, testing, dedicated backends, or low-level tools.

Low-level signing helps you understand transactions, but putting user private keys into application code is not a production pattern.

Verifying Signatures

After signing, use the SDK's verification method to check whether the transaction structure is correct:

TypeScript
1await tx.sign()
2const ok = await tx.verify()
3console.log(ok)

Common reasons for verification failure include:

  • The private key doesn't match the address of the previous output.
  • The source transaction or output index is incorrect.
  • The amount or script information is wrong.
  • The transaction content was modified after signing.
  • The fee or change leads to an unexpected structure.

A successful verification does not guarantee that broadcasting will succeed—network policies and service status also matter—but verification is a crucial check before broadcasting.

Common Beginner Misconceptions

  • A transaction does not necessarily have just one signature; each input may require its own unlocking proof.
  • Signing is not just signing the txid; it binds transaction content according to specific rules.
  • P2PKH unlocking needs a signature and a public key, not just an address.
  • Do not arbitrarily modify the transaction after signing—changing outputs, amounts, or scripts can invalidate signatures.
  • Private keys must never appear in frontend code, logs, screenshots, or tutorial repositories. Example WIFs must be placeholders or test-only.

References

Recommended articles