This guide explores the technical process of creating an Ordinals inscription transaction from a developer's perspective. We'll analyze the key components and workflow involved in minting inscriptions on the Bitcoin blockchain.
Core Concepts
Before diving into the code, let's clarify some essential terms:
OutPoint
Bitcoin transactions operate on the UTXO (Unspent Transaction Output) model, where each transaction output has a unique identifier called an OutPoint, consisting of:
TxHash: 32-byte hash of the previous transactionIndex: 4-byte integer indicating the output's position
SatPoint
Ordinals protocol distinguishes individual satoshis by:
OutPoint: Reference to the UTXOoffset: Position within the UTXO's satoshis
This combination uniquely identifies any satoshi on the Bitcoin blockchain.
The Inscription Process
Step 1: Preparation
First, we prepare the content to be inscribed and identify available UTXOs:
let inscription = Inscription::from_file(options.chain(), &self.file)?;
let mut utxos = index.get_unspent_outputs(Wallet::load(&options)?)?;Step 2: Transaction Parameters
The minting process uses these key parameters:
satpoint: Option<SatPoint>, // Target sat location
inscription: Inscription, // Content to inscribe
inscriptions: BTreeMap<SatPoint, InscriptionId>, // Existing inscriptions
network: Network, // Bitcoin network
BTreeMap<OutPoint, Amount>, // Available UTXOs
change: [Address; 2], // Change addresses
destination: Address, // Recipient address
commit_fee_rate: FeeRate, // Commit transaction fee
reveal_fee_rate: FeeRate, // Reveal transaction fee
no_limit: bool, // Default constraintsStep 3: Two-Phase Transaction Structure
Ordinals inscriptions use a commit/reveal pattern:
- Commit Transaction: Locks funds in a Taproot output containing the inscription script
- Reveal Transaction: Spends the commit output, revealing the inscription content on-chain
👉 Understand Taproot transactions in depth
Step 4: Creating Inscription Transactions
The core function create_inscription_transactions handles:
Selecting a SatPoint
let satpoint = if let Some(satpoint) = satpoint {
satpoint
} else {
utxos.keys()
.find(|outpoint| !inscribed_utxos.contains(outpoint))
.map(|outpoint| SatPoint {
outpoint: *outpoint,
offset: 0,
})?
};Validation Checks
The code verifies the selected UTXO hasn't been inscribed previously.
Step 5: Cryptographic Setup
Generates keys for the reveal transaction:
let secp256k1 = Secp256k1::new();
let key_pair = UntweakedKeyPair::new(&secp256k1, &mut rand::thread_rng());Step 6: Building the Reveal Script
Constructs the inscription content following Ordinals protocol:
script::Builder::new()
.push_slice(&public_key.serialize())
.push_opcode(opcodes::all::OP_CHECKSIG)
.push_opcode(opcodes::OP_FALSE)
.push_opcode(opcodes::all::OP_IF)
.push_slice(PROTOCOL_ID) // b"ord"
// ... content type and body pushed here
.push_opcode(opcodes::all::OP_ENDIF)Step 7: Taproot Construction
Creates a Taproot spend script with the reveal script as a leaf:
let taproot_spend_info = TaprootBuilder::new()
.add_leaf(0, reveal_script.clone())?
.finalize(&secp256k1, public_key)?;Step 8: Transaction Building
The process continues with:
- Calculating reveal fees
- Constructing the commit transaction
- Building the reveal transaction
- Validating output values
- Computing signatures
- Assembling witness data
👉 Learn about Schnorr signatures
Step 9: Broadcast
Finally, the signed transactions are broadcast:
let signed_raw_commit_tx = client.sign_raw_transaction(&unsigned_commit_tx)?;
let commit = client.send_raw_transaction(&signed_raw_commit_tx)?;
let reveal = client.send_raw_transaction(&reveal_tx)?;Key Technical Considerations
- Taproot Optimization: Leverages Schnorr signatures and MAST (Merklized Alternative Script Trees)
- Witness Data: Inscription content is stored in the witness field of reveal transactions
- Fee Management: Careful calculation of both commit and reveal transaction fees
- Dust Prevention: Ensures outputs meet minimum value requirements
FAQ Section
Q: What's the purpose of the two-phase commit/reveal process?
A: It allows inscription content to be securely committed to the blockchain while maintaining efficient verification through Taproot's features.
Q: How does Ordinals protocol differ from traditional Bitcoin transactions?
A: While using standard Bitcoin transactions, Ordinals adds a protocol layer that assigns serial numbers to satoshis and embeds arbitrary content through witness data.
Q: What types of content can be inscribed?
A: Any digital content including text, images, or other file types, though the protocol is most commonly used for text-based inscriptions.
Q: How are inscriptions identified on-chain?
A: Each inscription receives a unique ID based on its position in the blockchain (block height + transaction index).
Q: What's the role of Taproot in Ordinals inscriptions?
A: Taproot enables more efficient and private scripting, making it ideal for embedding ordinal data while minimizing blockchain bloat.
Q: Can inscribed satoshis be spent normally?
A: Yes, though doing so might transfer the inscription depending on how the satoshis are spent in subsequent transactions.
Conclusion
The Ordinals inscription process combines Bitcoin's UTXO model with Taproot's advanced scripting capabilities to create a system for numbering and inscribing individual satoshis. This technical deep dive reveals the sophisticated cryptographic and blockchain engineering behind what appears as a simple inscription mechanism.