Transactions: signed transfers you can verify
A transaction is a signed instruction: move this from here to there. The signature is what makes it trustworthy — anyone can verify it, but only the owner could have created it.
The big idea
A transaction records a transfer and carries a signature made from the sender's private key, which anyone can verify by recomputing it.
See it in code
A transaction is really just a record of a transfer: who's sending, who's receiving, and how much. Here it is as a dictionary — the raw shape before any signing:
# A transaction is a record: move an amount from one owner to another.
tx = {"from": "Alice", "to": "Bob", "amount": 5}
print("Transaction:", tx["from"], "->", tx["to"], tx["amount"])Transaction: Alice -> Bob 5
On its own this record proves nothing — anyone could type it. What makes it trustworthy is a signature that only Alice could create.
So Alice signs it: hash her private key together with every field of the transfer. The result is a signature bound to this exact transaction:
import hashlib
def h(text):
return hashlib.sha256(text.encode()).hexdigest()[:12]
private_key = "alice-secret"
tx = {"from": "Alice", "to": "Bob", "amount": 5}
# sign: hash the secret key together with every field
tx["signature"] = h(private_key + tx["from"] + tx["to"] + str(tx["amount"]))
print("Signature:", tx["signature"])Signature: 113bcdeec6dc
Because the signature folds in from, to, and amount, it commits to the whole transfer. Now anyone should be able to check it — that's the final step.
A verifier recomputes the signature the exact same way and checks it matches — a green light without ever seeing the secret:
import hashlib
def h(text):
return hashlib.sha256(text.encode()).hexdigest()[:12]
private_key = "alice-secret"
def sign(tx, key):
return h(key + tx["from"] + tx["to"] + str(tx["amount"]))
tx = {"from": "Alice", "to": "Bob", "amount": 5}
tx["signature"] = sign(tx, private_key)
# a verifier recomputes the signature the same way
valid = tx["signature"] == sign(tx, private_key)
print("Transaction:", tx["from"], "->", tx["to"], tx["amount"])
print("Signature:", tx["signature"])
print("Valid?", valid)Transaction: Alice -> Bob 5 Signature: 113bcdeec6dc Valid? True
The signature depends on every field — sender, recipient, amount. Tamper with any of them and the recomputed signature won't match, so valid flips to False. That's how a network rejects forged or altered transfers before they ever reach a block.
Signing data to guarantee it wasn't altered is everywhere: signed API requests, tamper-proof receipts, verified email (DKIM), software you install. The recipe — hash the important fields with a secret, verify by recomputing — is a reusable pattern for authenticity.
Try it yourself
Change the amount after signing and re-check valid — watch it fail, proving tampering is caught. Then bundle several transactions into a list, the way a real block holds many transfers.
The common mistake
Signing only part of the data. If the signature doesn't cover the amount, an attacker could change 5 to 500 and it would still 'verify'. Always sign every field that matters — the signature must commit to the whole transaction.
What it unlocks
Transactions build on wallets and keys and hashing, get recorded in blocks and chains, and use dictionaries.