Technical Bitcoin
Move from exact transaction and Script bytes through Taproot, block validation, peer messages, privacy, layer two systems and protocol engineering. Every route combines instruction, a worked example, deterministic execution and a transfer task.
Build the byte-level mental model
A Bitcoin implementation moves between human intent, typed values, canonical bytes, cryptographic commitments and independently validated state. The first workbench makes variable-length boundaries explicit before the larger transaction project.
- 01Represent values
Encode signed and unsigned integers, hashes, amounts and vectors without losing leading zeros or byte order.
- 02Serialize objects
Construct complete transactions, headers, scripts and network frames from typed fields.
- 03Commit and authorize
Recompute identifiers, Merkle roots, signature digests and spending predicates from those bytes.
- 04Validate state
Apply local policy, consensus checks and chain context without trusting a peer, miner or explorer.
Encode and parse CompactSize without ambiguity
Cross the fd boundary, decode canonical little-endian payloads, then reject short and unnecessarily long encodings.
See the same bits in every representation
One bounded value stays synchronized across hexadecimal, binary, decimal, text, byte order, signedness and bit-level edits. Hash, slice and concatenate the exact bytes without losing leading zeros.
Slice and concatenate bytes
Complete a tested programmer project
Build one raw transaction in TypeScript, Python, and Rust. Save the same typed fields, run each fixed serializer in isolation, and require Bitcoin Core to agree with the resulting bytes and transaction ID.
Construct, run, and verify one transaction
Edit typed fields. The selected language serializes them in an isolated process, then Bitcoin Core decodes the exact bytes on a temporary regtest node.
1. Edit the typed transaction
Project not saved yet.
2. Inspect the runnable source
// Run with Node 22+ after saving as transaction.mjs.
import { createHash } from "node:crypto";
const project = {
"version": 2,
"locktime": 0,
"inputs": [
{
"txid": "1111111111111111111111111111111111111111111111111111111111111111",
"vout": 1,
"sequence": 4294967293,
"scriptSigHex": ""
}
],
"outputs": [
{
"value": 50000,
"scriptPubKeyHex": "00142222222222222222222222222222222222222222"
},
{
"value": 18800,
"scriptPubKeyHex": "00143333333333333333333333333333333333333333"
}
]
};
const hex = (text) => Buffer.from(text, "hex");
const u32 = (number) => { const out = Buffer.alloc(4); out.writeUInt32LE(number); return out; };
const u64 = (number) => { const out = Buffer.alloc(8); out.writeBigUInt64LE(BigInt(number)); return out; };
const compact = (number) => number < 0xfd ? Buffer.from([number]) : (() => { throw new Error("Bounded example only"); })();
const parts = [u32(project.version), compact(project.inputs.length)];
for (const input of project.inputs) parts.push(Buffer.from(hex(input.txid)).reverse(), u32(input.vout), compact(hex(input.scriptSigHex).length), hex(input.scriptSigHex), u32(input.sequence));
parts.push(compact(project.outputs.length));
for (const output of project.outputs) parts.push(u64(output.value), compact(hex(output.scriptPubKeyHex).length), hex(output.scriptPubKeyHex));
parts.push(u32(project.locktime));
const raw = Buffer.concat(parts);
const txid = Buffer.from(createHash("sha256").update(createHash("sha256").update(raw).digest()).digest()).reverse().toString("hex");
console.log(JSON.stringify({ transactionHex: raw.toString("hex"), txid }, null, 2));
// Expected txid: a71281af068a7276b4b98e5b9c48995c36917019c921b3a12e15a92fb402d2673. Inspect the locally constructed object
- Transaction ID
- a71281af068a7276b4b98e5b9c48995c36917019c921b3a12e15a92fb402d267
- Serialized bytes
- 113
- Weight
- 452
- Output total
- 68,800 sats
Complete transaction hex
020000000111111111111111111111111111111111111111111111111111111111111111110100000000fdffffff0250c300000000000016001422222222222222222222222222222222222222227049000000000000160014333333333333333333333333333333333333333300000000Construct and decode real objects
Edit the fixtures directly. Each result is generated from the displayed input and malformed boundaries are rejected.
Use the protocol primitives
Move from raw bytes to a complete transaction, Script trace, PSBT or block header without leaving the Guild.
Reverse Bytes
Reverse complete hexadecimal bytes without reversing the characters inside each byte.
Change any field, predict what should change, then run the utility.
Safety boundary: This learning surface does not store inputs. Never paste a live seed phrase, private key, wallet backup, or confidential transaction.
Continue into protocol engineering
Each route adds real vectors, mutation tests, failure analysis and a transfer task.
Bitcoin Script
Execute locking conditions one opcode and stack item at a time.
10 lessonsSegWit, Taproot, and modern signing
Understand the upgrades that made modern Bitcoin protocols practical.
9 lessonsMining, blocks, and consensus
Follow a transaction from gossip to deeply buried block state.
14 lessonsNodes, networking, and Bitcoin Core
Operate and reason about the software enforcing the rules.
10 lessonsPrivacy, security, and threat modelling
Protect users against both cryptographic and operational failure.
8 lessonsLightning, channels, DLCs, and layered systems
Understand off-chain enforcement and its on-chain escape hatches.
8 lessonsData protocols, Ordinals, Runes, and BitVM
Analyze protocols built from Bitcoin transaction semantics.
7 lessonsMarkets, financialization, and risk
Connect protocol mechanics to real financial systems without confusing forecasts with facts.
8 lessonsBitcoin protocol engineering
Move from understanding to building, testing, and reviewing.
8 lessons