Jhonatan Pinheiro
Carregando página...
Jhonatan Pinheiro
Carregando página...
Jhonatan Pinheiro
Carregando página...
Teoria ordinal, o envelope de uma inscrição campo a campo, commit e reveal, o runestone byte a byte, etching com commitment, edicts, cenotaph e os erros que custam caro.
O Bitcoin não tem contrato inteligente. Não tem estado global, não tem laço, não tem armazenamento persistente por conta. Ainda assim, desde 2023 ele carrega imagens, textos, coleções e tokens fungíveis.
Isso não aconteceu por uma mudança de consenso. Aconteceu porque dois recursos criados para outra finalidade — o desconto de peso do SegWit e o script path do Taproot — abriram, sem querer, espaço barato para dados arbitrários. Ordinals e Runes são o que se construiu em cima disso.
Este guia é a documentação que eu queria ter encontrado: o formato dos bytes, as regras exatas, os comandos e os erros que custam dinheiro.
Três conceitos precisam estar firmes, senão o resto não faz sentido.
O Bitcoin não guarda saldo por endereço. Ele guarda saídas não gastas — cada uma com um valor em satoshis e uma condição de gasto. Uma transação consome saídas inteiras e cria novas.
bitcoin-cli listunspent
# [{ "txid": "abc...", "vout": 0, "amount": 0.00015000, "confirmations": 12 }]
bitcoin-cli gettxout "abc..." 0Como cada saída tem origem rastreável, é possível seguir frações específicas de moeda através da história. É disso que a teoria ordinal se aproveita.
O SegWit (2017) separou a assinatura do corpo da transação e criou a unidade de peso (weight unit, WU):
O tamanho virtual (vsize), que é o que define a taxa, é o peso dividido por 4. Na prática: dado no witness custa um quarto.
bitcoin-cli decoderawtransaction "<hex>" | jq '{size, vsize, weight}'
# size: tamanho total em bytes
# weight: 4 * (bytes do corpo) + 1 * (bytes do witness)
# vsize: weight / 4 <- é isto que multiplica a taxa em sats/vBO Taproot (2021) permite gastar uma saída revelando um script guardado em uma árvore. Esse script vai no witness — e não tem limite prático de tamanho imposto pelo consenso.
Junte as duas coisas: um script Taproot pode carregar um arquivo inteiro, pagando um quarto da taxa. Nenhuma das duas atualizações foi proposta com esse objetivo. A consequência foi emergente — e é a raiz de toda a discussão que veio depois.
A teoria ordinal, criada por Casey Rodarmor, é uma convenção de numeração. Ela não altera o Bitcoin em nada: é uma regra que qualquer pessoa pode aplicar sobre a cadeia e chegar ao mesmo resultado.
Regra 1 — atribuição. Satoshis são numerados de 0 a 2.099.999.997.690.000, na ordem em que são minerados. Os satoshis do subsídio de um bloco recebem os próximos números disponíveis.
Regra 2 — transferência. Satoshis passam pelas transações em ordem primeiro a entrar, primeiro a sair (FIFO). Concatene as entradas na ordem em que aparecem; distribua para as saídas na ordem em que aparecem.
# Entradas: [ A: sats 100-199 ] [ B: sats 500-549 ]
# Saídas: [ X: 120 sats ] [ Y: 30 sats ]
# X recebe: 100-199 (100 sats) + 500-519 (20 sats)
# Y recebe: 520-549 (30 sats)Regra 3 — taxas. Os satoshis pagos como taxa vão para o minerador e entram na saída da coinbase, logo depois dos satoshis do subsídio. É por isso que gastar um sat inscrito como taxa entrega sua inscrição ao minerador.
Vale insistir: nada disso é validado pela rede. Um nó Bitcoin não sabe o que é um ordinal. Quem sabe é o indexador que roda essa regra. Duas implementações divergentes produziriam saldos divergentes — a convenção só vale porque todos usam a mesma.
O mesmo satoshi pode ser escrito de cinco formas. Todas identificam o mesmo número.
| Notação | Exemplo | O que expressa |
|---|---|---|
| Inteiro | 2099994106992659 | O número de série puro |
| Decimal | 3891094.16797 | bloco.offset — bloco de origem e posição dentro dele |
| Grau | 1°111094′214″16797‴ | Ciclo, época, período de dificuldade e posição |
| Percentil | 99.99971949060254% | Posição relativa no supply total |
| Nome | satoshi | Codificação em base 26 (letras a–z) |
A notação de grau é a que revela a raridade, porque expõe os quatro ciclos do Bitcoin de uma vez:
A°B′C″D‴
# A = ciclo (a cada 6 halvings, quando halving e ajuste coincidem)
# B = índice do bloco na época de halving (0 a 209.999)
# C = índice do bloco no período de dificuldade (0 a 2.015)
# D = índice do satoshi dentro do blocoDaí sai a escala de raridade:
| Raridade | Condição | Quantidade aproximada |
|---|---|---|
| Common | Qualquer sat que não seja o primeiro do bloco | ~2,1 quatrilhões |
| Uncommon | Primeiro sat de cada bloco | ~6,9 milhões |
| Rare | Primeiro sat de cada ajuste de dificuldade | ~3.400 |
| Epic | Primeiro sat de cada época de halving | 32 |
| Legendary | Primeiro sat de cada ciclo | 5 |
| Mythic | Primeiro sat do bloco gênesis | 1 |
ord find 2099994106992659 # em que UTXO está este sat
ord list <outpoint> # que sats estão neste UTXO
ord traits 2099994106992659 # raridade, ciclo, época, notaçõesA raridade é uma propriedade da convenção, não do protocolo. Um sat "rare" é indistinguível de qualquer outro para o consenso do Bitcoin — vale o que o mercado que aceita a convenção decidir que vale.
Uma inscrição anexa conteúdo a um satoshi. O conteúdo vai dentro de um envelope no script Taproot revelado no gasto:
OP_FALSE
OP_IF
OP_PUSH "ord" # marcador do protocolo
OP_PUSH 0x01 # tag 1 = content-type
OP_PUSH "image/png"
OP_PUSH 0x00 # tag 0 = corpo (body)
OP_PUSH <bytes ...> # conteúdo, em pedaços de até 520 bytes
OP_PUSH <bytes ...>
OP_ENDIFTrês detalhes que explicam por que isso funciona:
OP_FALSE OP_IF ... OP_ENDIF cria um bloco que nunca executa. Para o consenso, é dado inerte — não custa validação, não quebra nada.A inscrição é atribuída ao primeiro satoshi da primeira saída da transação de reveal — a menos que um ponteiro diga outra coisa (tag 2, adiante).
O identificador de uma inscrição é o txid do reveal seguido do índice:
6fb976ab49dcec017f1e201e84395983204ae1a7c2abf7ced0a85d692e442799i0
# <txid do reveal> + "i" + <índice da inscrição naquela transação>Depois do marcador "ord", o envelope carrega pares de tag e valor. As tags são números:
| Tag | Campo | Para que serve |
|---|---|---|
| 0 | body | O conteúdo em si (sempre por último) |
| 1 | content-type | Tipo MIME: image/png, text/plain;charset=utf-8, text/html |
| 2 | pointer | Em que sat da transação a inscrição deve cair |
| 3 | parent | Id da inscrição-pai (procedência) |
| 5 | metadata | Metadados em CBOR |
| 7 | metaprotocol | Nome de um protocolo que roda por cima (ex.: BRC-20) |
| 9 | content-encoding | Codificação do corpo, como gzip |
| 11 | delegate | Id de outra inscrição que fornece o conteúdo |
A convenção de paridade importa: tags pares são essenciais — um indexador que não reconhece uma delas deve tratar a inscrição como inválida; tags ímpares podem ser ignoradas com segurança por versões antigas. É o mecanismo que permite evoluir o protocolo sem quebrar quem não atualizou.
# Conteúdo comprimido, para caber mais barato
OP_PUSH 0x09 OP_PUSH "gzip"
OP_PUSH 0x01 OP_PUSH "text/html;charset=utf-8"
OP_PUSH 0x00 OP_PUSH <html comprimido>Inscrever exige duas transações. Entender isso evita a maior parte dos erros operacionais.
# 1) COMMIT
# Cria uma saída Taproot cujo endereço compromete a árvore de scripts,
# e um dos ramos dessa árvore é o envelope com o seu conteúdo.
# Nesta etapa NADA do conteúdo aparece na cadeia.
# 2) REVEAL
# Gasta aquela saída pelo script path, revelando o ramo do envelope.
# É aqui que os bytes entram na blockchain, no witness.Consequências práticas:
ord wallet inscribe --fee-rate 15 --file arte.png
# {
# "commit": "e2f1...",
# "reveal": "6fb9...",
# "inscriptions": [{ "id": "6fb9...i0", "location": "6fb9...:0:0" }],
# "total_fees": 24310
# }Limite de relay. Por padrão, um nó só repassa transações com até 400.000 unidades de peso (-maxstandardtxweight). Passando disso, a transação é válida mas não circula pela rede: só entra em bloco se for entregue direto a um minerador.
Limite de bloco. 4.000.000 WU. Uma única inscrição pode, em tese, ocupar quase um bloco inteiro.
A conta da taxa. Como witness custa 1 WU por byte:
# Estimativa para um arquivo de 100 KB no witness
# 100.000 bytes * 1 WU = 100.000 WU
# vsize = 100.000 / 4 = 25.000 vB
# a 20 sats/vB -> 25.000 * 20 = 500.000 sats de taxa (só o reveal)
bitcoin-cli estimatesmartfee 6 | jq '.feerate' # BTC/kvB
bitcoin-cli getmempoolinfo | jq '{size, bytes, mempoolminfee}'Regra de bolso: o mesmo arquivo no corpo da transação custaria quatro vezes mais. É o desconto do witness que torna a prática economicamente viável — e é exatamente por isso que parte da comunidade considera o desconto um erro de projeto.
Reduzir custo, na prática:
# 1. Comprima antes (tag 9 = content-encoding)
gzip -9 -c pagina.html > pagina.html.gz
# 2. Prefira formatos enxutos: SVG e HTML costumam ganhar de PNG
# 3. Use recursão: referencie bibliotecas já inscritas em vez de reinscrevê-las
# 4. Inscreva em lote: uma transação para várias inscrições
ord wallet batch --fee-rate 8 --batch lote.yamlA tag 3 aponta para uma inscrição-pai. Para que a filiação seja válida, a inscrição-pai precisa ser gasta na transação de reveal — ou seja, só quem controla o pai consegue criar um filho. É assim que uma coleção prova autenticidade sem registro central.
# lote.yaml — coleção com procedência
mode: separate-outputs
parent: 6fb9...i0
inscriptions:
- file: 001.png
- file: 002.png
- file: 003.pngA tag 11 faz uma inscrição apontar para o conteúdo de outra. Mil itens que compartilham a mesma imagem podem ser mil inscrições minúsculas delegando a uma só — o custo despenca.
Uma inscrição pode buscar outra através do endpoint /content/<id>. Isso permite inscrever uma biblioteca uma vez e reutilizá-la:
<script src="/content/6fb9...i0"></script>
<img src="/content/a1b2...i0">Outros endpoints de recursão expõem dados da própria cadeia, o que permite arte generativa que reage ao estado do Bitcoin:
/r/blockheight # altura atual
/r/blockhash # hash do bloco
/r/blocktime # timestamp
/r/sat/<numero> # inscrições naquele sat
/r/children/<id> # filhos de uma inscriçãoA tag 5 carrega metadados em CBOR — binário, mais compacto que JSON:
# {"nome": "Peça 001", "autor": "Jhonatan", "edicao": 1}
# vira alguns bytes de CBOR no envelope
ord wallet inscribe --fee-rate 10 --file arte.png --json-metadata meta.jsonUm satoshi pode receber mais de uma inscrição. As posteriores são reconhecidas, mas ficam claramente marcadas como reinscrições — a primeira é a canônica.
Nos primeiros meses, algumas inscrições foram criadas de formas que a implementação de referência não previa: envelopes duplicados na mesma entrada, tags desconhecidas, estruturas fora do padrão.
Em vez de descartá-las, o ord passou a numerá-las com números negativos — as cursed inscriptions. Era um sinal de "isto existe, mas não é canônico".
No bloco 824.544, o chamado jubileu encerrou essa distinção: a partir dali, as formas antes amaldiçoadas passaram a receber numeração positiva normal. As antigas mantiveram seus números negativos como registro histórico.
ord list <outpoint> | jq '.inscriptions'
# número negativo = inscrição anterior ao jubileu, criada de forma não canônicaA lição de engenharia aqui é boa: em protocolo baseado em convenção, mudar a regra é decisão social, não técnica. Não houve fork — houve acordo entre quem roda o indexador.
# Pré-requisito: nó completo com índice de transações
bitcoind -daemon -txindex=1
bitcoin-cli getblockchaininfo | jq '{blocks, initialblockdownload}'
# Indexador (a primeira indexação demora horas e ocupa dezenas de GB)
ord --index-sats server --http-port 8080
# Carteira
ord wallet create # gera e mostra a seed — guarde offline
ord wallet receive # endereço para depositar
ord wallet balance # separa cardinal (gastável) de ordinal
ord wallet outputs # todos os UTXOs
ord wallet inscriptions # o que você tem inscrito# Inscrever
ord wallet inscribe --fee-rate 12 --file arte.png
ord wallet inscribe --fee-rate 12 --file arte.png --destination bc1p...
ord wallet inscribe --fee-rate 12 --file arte.png --postage 10000sat
# Enviar
ord wallet send --fee-rate 10 bc1p... 6fb9...i0 # por id da inscrição
ord wallet send --fee-rate 10 bc1p... 2099994106992659 # por número de sat
# Consultar
ord wallet transactions
ord decode --txid <txid> # mostra o envelope decodificadoO parâmetro --postage merece atenção: é quantos satoshis acompanham a inscrição na saída. O padrão é 10.000 sats. Valores muito baixos podem cair abaixo do limite de poeira e travar transferências futuras.
Antes das Runes, criar token fungível no Bitcoin significava usar BRC-20: inscrições com um JSON dizendo o que se queria fazer.
{"p":"brc-20","op":"deploy","tick":"ordi","max":"21000000","lim":"1000"}
{"p":"brc-20","op":"mint","tick":"ordi","amt":"1000"}
{"p":"brc-20","op":"transfer","tick":"ordi","amt":"100"}Funciona, mas com três defeitos sérios:
Runes, lançado por Casey Rodarmor no bloco 840.000 (o halving de abril de 2024), foi desenhado para resolver isso sendo nativo ao modelo UTXO: o saldo mora no próprio UTXO, e a instrução vai em uma única saída OP_RETURN.
Um runestone é uma saída OP_RETURN assim:
OP_RETURN
OP_13 # marcador do protocolo Runes (OP_PUSHNUM_13)
<push de dados> # payload
<push de dados> # (se houver mais de um, concatene na ordem)O payload é uma sequência de inteiros LEB128 (varint de tamanho variável), lidos como pares tag → valor:
| Tag | Nome | Função |
|---|---|---|
| 0 | Body | A partir daqui vêm os edicts |
| 1 | Divisibility | Casas decimais (0 a 38) |
| 2 | Flags | Bits que ligam etching, terms e turbo |
| 3 | Spacers | Onde entram os separadores visuais do nome |
| 4 | Rune | O nome, codificado em base 26 |
| 5 | Symbol | Um caractere Unicode |
| 6 | Premine | Quantidade reservada ao criador |
| 8 | Cap | Número máximo de mints |
| 10 | Amount | Quantidade entregue por mint |
| 12 / 14 | HeightStart / HeightEnd | Janela de mint por altura absoluta |
| 16 / 18 | OffsetStart / OffsetEnd | Janela de mint relativa ao etching |
| 20 | Mint | Id da rune que está sendo cunhada |
| 22 | Pointer | Saída que recebe o saldo não alocado |
A mesma regra de paridade das inscrições vale aqui: tag par desconhecida invalida o runestone (vira cenotaph); tag ímpar desconhecida é ignorada. É o que permite adicionar recursos no futuro sem quebrar indexadores antigos.
# Decodificar um runestone da cadeia
ord decode --txid <txid>
bitcoin-cli getrawtransaction <txid> 1 | jq '.vout[] | select(.scriptPubKey.type=="nulldata")'Por que OP_RETURN e não witness? Porque OP_RETURN é provadamente não gastável — os nós podem descartar essas saídas do conjunto de UTXOs, sem inchar a memória da rede. Custa mais caro por byte (fica no corpo, sem desconto), e é justamente por isso que o formato é binário e enxuto.
Etching é o ato de criar a rune. Define nome, símbolo, divisibilidade, premine e as regras de cunhagem.
# etching.yaml
mode: separate-outputs
etching:
rune: MINHA•PRIMEIRA•RUNE
divisibility: 2
premine: 1000
symbol: ¤
supply: 21000
terms:
amount: 100
cap: 200
height: [840000, 900000] # janela absoluta (opcional)
turbo: true
inscriptions:
- file: logo.png # opcional: um "cenotaph visual" da runeord wallet batch --fee-rate 20 --batch etching.yamlAqui está o detalhe que mais confunde quem etcha pela primeira vez: o nome da rune precisa ser comprometido antes, e o compromisso precisa ter pelo menos 6 confirmações antes da transação de etching.
O motivo é anti-front-running: sem isso, qualquer um veria seu nome no mempool e publicaria a mesma rune com taxa maior. Com o commitment, quem tenta copiar precisaria esperar seis blocos — tempo suficiente para o original confirmar.
# Na prática, o ord faz isso por você e espera:
# "Waiting for rune commitment ... to mature (6 confirmations)"
# São ~1 hora entre iniciar e a rune existir. Não interrompa o processo.premine + (amount × cap).# premine 1000 + (100 * 200) = 21.000 unidades
# Quanto maior a fatia do premine, mais centralizada é a distribuição.Premine é o primeiro número que qualquer pessoa deveria olhar antes de tocar em uma rune. Premine de 90% significa que o criador detém quase tudo.
O campo Flags (tag 2) é um conjunto de bits:
| Bit | Nome | Significado |
|---|---|---|
| 0 | Etching | Esta transação cria uma rune |
| 1 | Terms | O etching define regras de mint aberto |
| 2 | Turbo | A rune adere automaticamente a futuras mudanças do protocolo |
Nomes de rune usam apenas letras maiúsculas de A a Z, codificadas como um inteiro em base 26 modificada.
MINHA•PRIMEIRA•RUNE
# A identidade real é MINHAPRIMEIRARUNE.
# Os "•" são spacers (tag 3): decoração visual, não fazem parte do nome.
# Não existem duas runes com o mesmo nome, ainda que com spacers diferentes.Se todos os nomes estivessem disponíveis no lançamento, os curtos teriam sido tomados nos primeiros blocos. O protocolo evita isso liberando os nomes ao longo do tempo:
# Comprimento mínimo pela altura atual, na prática:
# 840.000 -> 13 letras
# 857.500 -> 12 letras
# 875.000 -> 11 letras
# ...
# 1.050.000 -> 1 letraRunes etchadas antes do bloco 840.000 não existem: a primeira rune possível é a do próprio bloco de lançamento.
Cada rune é identificada pelo bloco e pela posição da transação dentro dele:
840000:1 # bloco 840.000, transação 1
# É este id que os edicts usam — não o nome.Se o etching definiu terms, qualquer pessoa pode cunhar até o cap ser atingido, dentro da janela permitida.
ord wallet mint --fee-rate 15 --rune MINHA•PRIMEIRA•RUNE
# Um mint é um runestone com a tag Mint (20) apontando o rune id:
# OP_RETURN OP_13 <20> <bloco> <tx>Regras que valem conhecer:
amount. Não existe mint parcial.height ou offset) não é erro de transação: ele simplesmente não produz nada, e a taxa foi paga do mesmo jeito.cap, mints deixam de produzir. Em runes populares isso vira corrida por bloco, e é o que costuma explodir a taxa da rede em janelas de mint.height) usa números absolutos de bloco; janela por offset (offset) conta a partir do bloco do etching.Transferência é um edict dentro do runestone: uma tripla (id, quantidade, saída).
ord wallet send --fee-rate 12 bc1p... 500:MINHA•PRIMEIRA•RUNEDuas convenções fazem toda a diferença:
OP_RETURN". É o mecanismo de airdrop em uma transação só.# Transação com 3 saídas (índices 0, 1, 2) mais o OP_RETURN:
# edict (840000:1, 0, 3) -> divide todo o saldo entre as saídas 0, 1 e 2O que sobra sem edict explícito vai para a primeira saída não-OP_RETURN. A tag Pointer (22) muda esse destino:
# Sem pointer: o troco de runes cai na saída 0
# Com pointer = 2: o troco cai na saída 2Esquecer disso é uma das formas mais fáceis de mandar saldo para o endereço errado — inclusive para o do destinatário, quando você queria só o troco.
Um cenotaph é um runestone malformado. O nome não é acidente: cenotáfio é um túmulo vazio.
Um runestone vira cenotaph quando:
E a consequência é dura, deliberadamente:
| Situação | O que acontece |
|---|---|
| Runes entrando na transação | São queimadas |
| O runestone continha um etching | A rune é criada com supply zero e não pode ser cunhada |
| O runestone continha um mint | O mint conta contra o cap, mas as unidades são queimadas |
Por que tão severo? Porque a alternativa — ignorar o erro e seguir — abriria espaço para divergência entre indexadores. Queimar é determinístico e igual para todos. O custo dessa escolha é que um bug na sua implementação destrói saldo real, sem recurso.
# Antes de assinar qualquer transação de runes construída à mão:
ord decode --txid <txid> # o ord aponta cenotaph
# Em caso de dúvida, teste em signet primeiro. Sempre.Como nem Ordinals nem Runes são validados pelo consenso, todo saldo depende de um indexador. Rodar o seu é a única forma de não confiar em terceiros.
# Requisitos aproximados do ord com índice de sats
# - nó Bitcoin completo com -txindex=1 (~700 GB e crescendo)
# - dezenas de GB adicionais para o índice do ord
# - SSD é praticamente obrigatório; a indexação inicial leva horas
ord --index-sats --index-runes server --http-port 8080
curl -s localhost:8080/status | jqEndpoints úteis da API local:
curl -s localhost:8080/inscription/<id> -H "Accept: application/json"
curl -s localhost:8080/output/<txid>:<vout> -H "Accept: application/json"
curl -s localhost:8080/rune/MINHA•PRIMEIRA•RUNE -H "Accept: application/json"
curl -s localhost:8080/sat/2099994106992659 -H "Accept: application/json"Existem APIs de terceiros que poupam o trabalho de indexar. Elas são convenientes e legítimas, mas note o que você está aceitando: a resposta delas é a fonte de verdade da sua aplicação. Para exibir, tudo bem; para decidir sobre custódia de valor, rode o seu índice.
O risco aqui raramente é criptográfico — é operacional.
# 1. Separe as carteiras. Sats inscritos e runes NUNCA na carteira do dia a dia.
ord wallet balance # cardinal = gastável | ordinal = protegido
# 2. Trave os UTXOs que carregam valor
bitcoin-cli lockunspent false '[{"txid":"<txid>","vout":0}]'
bitcoin-cli listlockunspent
# 3. Confira o destino antes de enviar
ord wallet send --dry-run --fee-rate 10 bc1p... 6fb9...i0Regras que evitam quase todo prejuízo:
bitcoind -signet -daemon
ord --signet --index-sats server
ord --signet wallet inscribe --fee-rate 1 --file teste.txtOrdinals e Runes são, tecnicamente, um caso de estudo notável: dois protocolos completos construídos sem alterar uma linha do consenso, aproveitando recursos que existiam para outra finalidade.
Isso traz uma característica que precisa estar clara antes de qualquer coisa: o Bitcoin não conhece nenhum dos dois. Não existe validação de saldo, não existe mensagem de erro amigável, não existe reversão. Existe uma convenção que vale enquanto os indexadores concordarem — e um conjunto de regras implacáveis quando você erra a codificação.
Para quem programa, o valor de estudar isso vai além do assunto: é um exemplo raro de protocolo bem documentado, com decisões de projeto explícitas (por que OP_RETURN e não witness, por que queimar em vez de ignorar, por que paridade de tags) e consequências verificáveis na cadeia. Dá para ler o código, reproduzir cada byte em signet e conferir.
E, se for tocar em mainnet: signet primeiro, carteira separada sempre, e nada de valor sem hardware wallet.