Introdução
No coração do mecanismo de validação de transações do Bitcoin reside uma linguagem de script própria, conhecida como Bitcoin Script. Diferente de linguagens de propósito geral, o Bitcoin Script foi projetado com características altamente restritivas: não é Turing-complete, mas sim determinístico e stack-based, ou seja, opera sobre uma pilha de dados em estilo LIFO (Last-In, First-Out). Essa pilha funciona como uma estrutura de dados onde os elementos são empilhados e desempilhados conforme os opcodes vão sendo executados.
Essa arquitetura limitada garante previsibilidade, segurança e ausência de loops infinitos, tornando-a ideal para a função que desempenha: definir e verificar as condições de gasto dos outputs de transações. Ao eliminar loops e chamadas recursivas, evita-se qualquer possibilidade de execução indefinida ou DoS (Denial-of-Service) via scripts maliciosos, algo que seria catastrófico em uma rede consensual e distribuída como o Bitcoin.
Em sua essência, o Bitcoin Script é um sistema de verificação declarativa, não executa comandos imperativos, mas sim validações que determinam se determinada transação pode ou não gastar um certo output. Cada script deve retornar true
ou false
, e qualquer erro de execução como pilha vazia, falha em verificação de assinatura, ou uso de opcodes inválidos resulta na invalidação da transação. Este sistema binário, válido ou inválido, constitui a espinha dorsal dos contratos inteligentes no Bitcoin, ainda que em sua forma mais austera e segura.
Diferentemente de contratos complexos em plataformas como Ethereum, que são projetados para serem generalistas e executáveis como máquinas de estados completas, os contratos no Bitcoin são verificáveis localmente, sem ambiguidade, e economicamente previsíveis em termos de custos computacionais.
OP_Codes: A Gramática do Bitcoin Script
Os opcodes (operation codes) são os elementos básicos da linguagem Bitcoin Script. Cada opcode representa uma instrução atômica que é executada sobre a pilha de dados. Eles funcionam como a gramática da linguagem, definindo desde operações lógicas e aritméticas até verificações criptográficas e controle de fluxo.
Ao todo, o conjunto de opcodes é intencionalmente limitado, com diversas instruções desativadas por motivos de segurança. Os opcodes são sempre interpretados sequencialmente pelo interpretador do Script, e qualquer erro ou resultado inesperado, como stack underflow, invalida a execução.
Os opcodes podem ser classificados em algumas categorias principais:
Operações de Pilha
Exemplos:
OP_DUP
,OP_DROP
,OP_SWAP
Função: Manipulam os elementos na pilha principal do script.
Ex: duplicar o topo da pilha (OP_DUP
), remover o topo (OP_DROP
), ou trocar as duas primeiras posições (OP_SWAP
).
Operações Aritméticas
Exemplos:
OP_ADD
,OP_SUB
,OP_EQUAL
Função: Realizam operações matemáticas básicas e comparações.
Ex: somar (OP_ADD
), subtrair (OP_SUB
), ou verificar igualdade (OP_EQUAL
).
Operações Lógicas
Exemplos:
OP_NOT
,OP_BOOLAND
Função: Avaliam expressões booleanas.
Ex: inverter valores lógicos (OP_NOT
) ou aplicar operador AND lógico (OP_BOOLAND
).
Criptografia
Exemplos:
OP_HASH160
,OP_CHECKSIG
Função: Executam funções de hash (como
SHA256 + RIPEMD160
) e validam assinaturas digitais.
Ex: verificar se uma assinatura corresponde à chave pública (OP_CHECKSIG
).
Controle de Fluxo
Exemplos:
OP_IF
,OP_ELSE
,OP_ENDIF
Função: Permitem construções condicionais.
Ex: executar uma parte do script somente se determinada condição for verdadeira.
Timelocks
Exemplos:
OP_CHECKLOCKTIMEVERIFY
,OP_CHECKSEQUENCEVERIFY
Função: Introduzem restrições temporais ao gasto de outputs.
Ex: impedir que um output seja gasto antes de certo bloco (OP_CHECKLOCKTIMEVERIFY
), ou após um delay relativo (OP_CHECKSEQUENCEVERIFY
).
Conversão e Codificação
Exemplos:
OP_NUM2BIN
,OP_BIN2NUM
Função: Manipulam a forma como inteiros são representados em binário.
Ex: converter número para formato binário fixo (OP_NUM2BIN
) e vice-versa (OP_BIN2NUM
).
Um exemplo clássico de uso de opcodes é o script de um pagamento P2PKH:
OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
Aqui, cada opcode cumpre uma função específica no processo de autenticação:
OP_DUP
: Duplica a chave pública.OP_HASH160
: AplicaSHA256
seguido deRIPEMD160
.OP_EQUALVERIFY
: Verifica se o hash corresponde ao esperado.OP_CHECKSIG
: Valida a assinatura digital contra a chave pública e o conteúdo da transação.
A execução é puramente determinística e ocorre localmente, sem necessidade de comunicação externa ou acesso a memória compartilhada.
Essa estrutura baseada em opcodes torna o Bitcoin Script semelhante a linguagens de máquina virtual minimalistas, como a Forth, mas adaptada para um ambiente de consenso distribuído onde eficiência, segurança e verificabilidade são prioridades absolutas.
ScriptPubKey e ScriptSig
Toda transação no Bitcoin que gasta um output carrega dois scripts:
ScriptPubKey (ou "locking script"): incluído no output da transação anterior. Define as condições que devem ser satisfeitas para gastar aquele valor. Ele atua como um "cofre" com uma combinação específica
ScriptSig (ou "unlocking script"): incluído no input da transação atual. Apresenta os dados que satisfazem as condições estabelecidas pelo ScriptPubKey. É como a "chave" que tenta abrir o cofre.
A execução do script ocorre concatenando o ScriptSig com o ScriptPubKey e executando ambos na pilha:
ScriptSig <concat> ScriptPubKey
O processo de avaliação consiste em executar todos os opcodes sequencialmente, manipulando a pilha a cada passo. Se, ao final da execução completa, restar exatamente um valor booleano true
no topo da pilha, o input da transação é considerado válido, ou seja, os bitcoins estão autorizados a serem gastos. Qualquer outro resultado como pilha vazia, valor false
, ou erro de execução, invalida o input.
Esse modelo pode ser pensado como um modelo de máquina de verificação determinística onde o ScriptPubKey representa o programa de validação e o ScriptSig os dados de entrada. A grande vantagem disso é que cada node da rede pode verificar independentemente se uma transação cumpre as condições de gasto, sem depender de estados externos ou efeitos colaterais.
Ordem de Execução
Embora os scripts sejam logicamente separados, sua execução ocorre como se fossem um único script concatenado. Isso significa que o resultado depende tanto dos dados fornecidos pelo ScriptSig quanto da lógica codificada no ScriptPubKey. Essa distinção é crucial para entender ataques, vulnerabilidades como a maleabilidade, e inovações como SegWit e Taproot, que reorganizam essa estrutura de forma mais segura e eficiente.
Stack Principal VS Stack Alternativa
Bitcoin Script utiliza uma pilha principal, mas também disponibiliza uma pilha alternativa (alt stack
), acessível via alguns opcodes específicos como OP_TOALTSTACK
e OP_FROMALTSTACK
. Embora seu uso seja raro em scripts convencionais como P2PKH ou P2SH, ela permite construções mais sofisticadas em scripts customizados, algo relevante para desenvolvedores que exploram modelos de multisig, HTLCs ou vaults.
Segurança por Simplicidade
O design do sistema de scripts, com sua separação clara entre ScriptSig e ScriptPubKey, promove segurança e auditabilidade. Cada parte é auditável de forma independente, o locking script pode ser analisado publicamente no UTXO, enquanto o unlocking script é visível apenas no momento do gasto. Com SegWit e Taproot, essa divisão foi ainda mais refinada, otimizando o que é visível onchain e o que pode ser revelado seletivamente.
Casos Práticos
Analisando os Scripts nos Principais Tipos de Transação
1. Pay-to-PubKey-Hash (P2PKH)
O P2PKH é o formato de transação mais comum no Bitcoin Legacy. O endereço corresponde ao hash160 da chave pública do destinatário.
ScriptPubKey (output):
OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
ScriptSig (input):
<signature> <public key>
Execução:
OP_DUP
: duplica a chave pública.OP_HASH160
: aplicaSHA256
seguido deRIPEMD160
.<PubKeyHash>
: empilha o hash do ScriptPubKey.OP_EQUALVERIFY
: compara os hashes e aborta se forem diferentes.OP_CHECKSIG
: verifica a assinatura contra a chave pública.
Esse script garante que apenas quem possui a chave privada correspondente ao endereço pode gastar os fundos.
2. Pay-to-Script-Hash (P2SH)
Introduzido pelo BIP-16, o P2SH desloca a complexidade do script para o input, facilitando contratos mais sofisticados.
ScriptPubKey (output):
OP_HASH160 <RedeemScriptHash> OP_EQUAL
ScriptSig (input):
<sig1> <sig2> ... <RedeemScript>
Execução:
RedeemScript
é empilhado e sua hash160 é computada.Comparada ao hash no ScriptPubKey.
Se igual, o
RedeemScript
é executado com os dados anteriores como parâmetros.
O P2SH permite, por exemplo, multisigs e scripts com timelocks sem sobrecarregar o espaço do output.
3. Pay-to-Witness-PubKey-Hash (P2WPKH)
Parte da proposta SegWit (BIP-141), esse formato separa a assinatura do corpo da transação, mitigando a maleabilidade e aumentando a eficiência.
ScriptPubKey (output):
0 <20-byte PubKeyHash>
Script de versão witness: 0 é a versão, seguido de 20 bytes de hash da chave pública
Witness (não está no ScriptSig!):
<signature> <public key>
Execução:
O ScriptPubKey instrui o validador a construir dinamicamente um script equivalente a um P2PKH, utilizando os dados presentes na witness stack.
A execução real é semelhante ao P2PKH, mas com a diferença crucial de que os dados da witness não estão sujeitos a maleabilidade da transação, pois são segregados na estrutura da transação.
Legacy vs SegWit: Resolvendo a Maleabilidade
Transações Legacy embutem assinatura e dados no ScriptSig, o que permite que pequenas alterações na forma da assinatura alterem o txid
, um problema conhecido como transaction malleability.
O SegWit (Segregated Witness), proposto no BIP-141 e ativado em 2017, moveu esses dados para uma nova estrutura separada da transação, chamada witness. Isso teve três efeitos:
Eliminou a maleabilidade do
txid
, permitindo construção confiável de canais Lightning. Já que para a implementação Lightning identificar a abertura do canal na blockchain ou o que chamamos de endereço do canal (channel point), é necessário identificar o txid bem como seu índice de output dentro da transação.Melhorou a eficiência ao redefinir a forma de cálculo do peso da transação.
Criou espaço para novos tipos de scripts, como P2WSH e Taproot (BIP-341).
Timelocks e OP Codes de Bloqueio Temporal
O Bitcoin Script também permite a introdução de restrições temporais ao gasto de moedas, por meio de timelocks e opcodes específicos:
1. nLockTime
Campo no cabeçalho da transação que impede sua validação antes de determinado block height ou timestamp.
2. OP_CHECKLOCKTIMEVERIFY (CLTV)
Permite que um output só seja gasto após um determinado bloco ou tempo:
<timestamp> OP_CHECKLOCKTIMEVERIFY OP_DROP <restante do script>
3. OP_CHECKSEQUENCEVERIFY (CSV)
Permite relative timelocks, ou seja, restrições baseadas no tempo desde a confirmação do input:
<delay> OP_CHECKSEQUENCEVERIFY OP_DROP <restante do script>
Esses opcodes são fundamentais para contratos inteligentes baseados em tempo, como canais de pagamento Lightning, swaps atômicos e vaults de segurança.
Conclusão
O sistema de scripts do Bitcoin, embora deliberadamente restritivo, é um dos pilares de sua segurança e flexibilidade. Sua linguagem stack-based, sem loops ou recursão, não busca a completude de Turing, mas sim a verificabilidade determinística, essencial em um sistema de consenso distribuído.
Por meio de construções como P2SH, SegWit, CLTV e CSV, o Bitcoin Script permite a implementação de contratos inteligentes simples, porém poderosos e todos executáveis por qualquer node sem risco de travamentos ou inconsistências.
Essa arquitetura combina expressividade suficiente para modelos de uso real com limitações intencionais que reforçam a robustez da rede. Ao entender o ScriptPubKey, o ScriptSig e o fluxo de execução dos scripts, desenvolvedores ganham acesso direto ao mecanismo de custódia programável mais sólido da história monetária.