O
Bitcoin costuma ser chamado também de
dinheiro programável. Suas propriedades digitais permitem aos usuários um alto grau de flexibilidade quando se trata de definir condições em relação ao gasto dos fundos.
Quando falamos sobre Bitcoin, também falamos de
carteiras e
moedas. Mas também podemos pensar em carteiras como chaves, moedas como cheques e a
blockchain como uma sequência de muitas fileiras de cofres trancados. Cada cofre possui uma ranhura estreita, de modo que qualquer pessoa pode depositar cheques ou visualizar seu conteúdo. No entanto, somente o portador da chave tem acesso ao interior do cofre.
Quando o portador da chave deseja enviar dinheiro a outra pessoa, ele destranca o cofre. Ele faz um novo cheque que faz referência ao anterior (que é então destruído) e o tranca em outro cofre, que o destinatário pode abrir. Depois disso, um novo destinatário pode repetir o processo.
Neste artigo, discutiremos sobre o Script, a linguagem de programação interpretada por
nodes (nós) na rede Bitcoin. O Script é o que governa o mecanismo de bloqueio/desbloqueio mencionado no exemplo dos cofres.
Ainda sobre nossa analogia acima, podemos dizer que existem duas partes em cada transação – uma chave e uma fechadura. Você usa sua chave para abrir o cofre que contém o cheque a ser enviado. Em seguida, adiciona um novo cheque a um novo cofre, com um cadeado diferente. Para usar o novo cheque, será necessária uma outra chave.
Simples assim. Podemos encontrar algumas variações nos tipos de fechaduras de cada sistema. Alguns cofres podem exigir que você forneça
múltiplas chaves. Outros, que você prove que sabe um segredo. Existem diversas condições que podem ser definidas.
Nossa chave é o que chamamos de scriptSig. A fechadura é a scriptPubKey. Se examinarmos esses componentes, veremos que eles são, na verdade, bits de dados e blocos de códigos. Quando combinados, eles criam um pequeno programa.
Quando você faz uma transação, está transmitindo essa combinação para a rede. Cada node que a recebe, fará uma verificação do programa, que informa se a transação é válida ou não. Caso não seja válida, a transação será descartada e você não poderá gastar os fundos bloqueados.
Os cheques (moedas) que você mantém são chamados de
unspent transaction outputs (UTXOs), ou seja, outputs (saídas) de transações não gastas. Os fundos podem ser usados por qualquer pessoa que forneça a chave correspondente à fechadura. Mais especificamente, a chave é o scriptSig e a fechadura é o scriptPubKey.
Se os UTXOs estiverem em sua carteira, eles provavelmente terão uma condição que diz
somente a pessoa capaz de provar a propriedade desta chave pública pode desbloquear esses fundos. Para desbloqueá-los, você fornece um scriptSig que inclui uma
assinatura digital, usando a
chave privada associada à chave pública especificada no scriptPubKey. Tudo isso ficará mais claro em breve.
O Script é conhecido como uma linguagem stack-based. Isso significa que, quando lemos um conjunto de instruções, as colocamos no que pode ser considerado uma coluna vertical. Por exemplo, a lista A, B, C pode ser organizada em uma pilha (stack) com A na parte inferior e C no topo. Quando recebemos determinadas instruções, operamos em um ou mais elementos, começando do topo do stack.
Elementos A, B e C sendo adicionados e “retirados” do stack.
Podemos distinguir entre os dados (como
assinaturas,
hashes e chaves públicas) e as instruções (ou
opcodes). As instruções removem dados e fazem algo com eles. Temos abaixo um exemplo muito simples de um script:
<xyz> <md5 hasher> <d16fb36f0911f878998c136191af705e> <check if equal>
Em vermelho, temos os dados e em azul, os opcodes. Lemos da esquerda para a direita, então primeiro colocamos a string <xyz> no stack (na pilha). O próximo é o opcode <md5 hasher>. Este não existe no sistema Bitcoin, mas digamos que ele remove o elemento do topo do stack (<xyz>) e faz o hash usando o algoritmo MD5. Em seguida, o output (saída) é adicionado de volta ao stack. O output aqui é d16fb36f0911f878998c136191af705e.
Que coincidência! Nosso próximo elemento a ser adicionado é <d16fb36f0911f878998c136191af705e>, então agora nosso stack tem dois elementos idênticos. Por fim, o <check if equal> destaca dois elementos do topo e verifica se eles são iguais. Se forem, ele adiciona <1> ao stack. Caso contrário, ele adiciona <0>.
Chegamos ao final da nossa lista de instruções. Nosso script poderia ter falhado de duas maneiras – se o elemento restante fosse zero, ou se um dos operadores provocasse uma falha devido a condições não atendidas. No exemplo, não tínhamos nenhum operador desse tipo e obtivemos um elemento resultante diferente de zero (<1>), então o script do exemplo é válido. Essas regras também se aplicam a transações reais de Bitcoin.
Esse foi apenas um programa imaginário. Vejamos alguns exemplos reais.
O script Pay-to-Pubkey (P2PK) é incrivelmente simples. Envolve o bloqueio e vínculo de fundos a uma chave pública específica. Se quiser receber fundos através deste método, deverá fornecer ao remetente sua chave pública, em vez de um endereço Bitcoin.
A
primeira transação entre
Satoshi Nakamoto e Hal Finney em 2009 foi com script P2PK. Essa estrutura era muito usada nos primeiros dias do Bitcoin, mas atualmente, foi substituída em larga escala pelo Pay-to-Pubkey-Hash (P2PKH).
O script de bloqueio para uma transação P2PK segue o formato <public key> OP_CHECKSIG. Muito simples. Você deve ter imaginado que o OP_CHECKSIG verifica se há uma assinatura correspondente à chave pública fornecida. Sendo assim, nosso scriptSig será um <signature> simples. Lembre-se de que o scriptSig é a chave da fechadura.
Uma assinatura é adicionada ao stack, seguida por uma chave pública. O OP_CHECKSIG extrai os dois e verifica a assinatura em relação à chave pública. Se forem correspondentes, um <1> será adicionado ao stack. Caso contrário, se adiciona um <0>.
Por motivos que explicaremos na próxima seção, o P2PK não é muito utilizado atualmente.
O script Pay-to-Pubkey-Hash (P2PKH) é o tipo de transação mais comum atualmente. A menos que você esteja fazendo o download de um software antigo, é bem provável que sua carteira irá usar esse tipo, por padrão.
O scriptPubKey do P2PKH é o seguinte:
OP_DUP OP_HASH160 <public key hash> OP_EQUALVERIFY OP_CHECKSIG
Antes de introduzirmos o scriptSig, vamos analisar a função dos novos opcodes:
OP_DUP
O OP_DUP extrai o primeiro elemento e o duplica. Em seguida, ele adiciona ambos de volta ao stack. O objetivo desta etapa é criar uma cópia com a qual seja possível realizar operações, sem modificar o elemento original.
OP_HASH160
Esse script extrai o primeiro elemento e faz hash duas vezes com ele. A primeira etapa faz hash com o algoritmo SHA-256. Em seguida, é feito outro hash com o output (saída) do SHA-256, usando o algoritmo RIPEMD-160. O output resultante é adicionado de volta ao stack.
OP_EQUALVERIFY
O OP_EQUALVERIFY combina dois outros operadores – OP_EQUAL e OP_VERIFY. O operador OP_EQUAL extrai dois elementos e verifica se eles são idênticos. Se forem, ele adiciona 1 ao stack. Caso contrário, adiciona um 0. O OP_VERIFY verifica se o elemento superior e é True (ou seja, diferente de zero). Se não for, a transação falhará. Combinando os dois operadores, o OP_EQUALVERIFY provoca a falha da transação caso os dois elementos do topo do stack não sejam correspondentes.
Desta vez, o scriptSig será o seguinte:
<signature> <public key>
É necessário fornecer uma assinatura e a chave pública correspondente para desbloquear os outputs (saídas) P2PKH.
O GIF acima representa o que acontece. Não é muito diferente de um script P2PK. Estamos apenas adicionando uma etapa extra para verificar se a chave pública corresponde ao hash no script.
Entretanto, devemos fazer uma observação. Em um script de bloqueio P2PKH, a chave pública não é visível – só é possível ver seu hash. Se usarmos um
explorer de blockchain e analisarmos um output P2PKH que não foi gasto, não será possível determinar a chave pública. Ela só é revelada quando o receptor decide transferir os fundos.
Isso tem algumas vantagens. A primeira é que o hash da chave pública é mais fácil de transmitir do que uma chave pública completa. Satoshi o lançou em 2009 por esse exato motivo. O hash de chave pública é o que hoje conhecemos como endereço Bitcoin.
A segunda vantagem é que os hashes de chave pública podem fornecer uma camada adicional de segurança contra a
computadores quânticos. Como a chave pública não é conhecida até que os fundos sejam gastos, é ainda mais difícil de se descobrir a chave privada. Seria necessário reverter as duas etapas de hashing (RIPEMD-160 e SHA-256) para obtê-la.
O Pay-to-Script-Hash (P2SH) representa um desenvolvimento muito interessante para o
Bitcoin. Ele permite que o remetente bloqueie fundos no hash de um script – não é necessário saber o que o script realmente faz. Usaremos o seguinte hash SHA-256 como exemplo:
e145fe9ed5c23aa71fdb443de00c7d9b4a69f8a27a2e4fbb1fe1d0dbfb6583f1
Você não precisa saber o input do hash para bloquear fundos nele. No entanto, quem gasta os fundos, precisa fornecer o script usado para fazer o hash e precisa satisfazer as condições desse script.
O hash acima foi criado do seguinte script:
<multiply by 2> <4> <check if equal>
Se você quiser gastar as moedas vinculadas a esse scriptPubKey, não basta fornecer os comandos. Você também precisa de um scriptSig que faça com que o script concluído seja avaliado como True. Neste exemplo, é um elemento que você <multiply by 2> (multiplica por dois) para dar um resultado de <4>. Claro, isso significa que nosso scriptSig é apenas <2>.
Na realidade, o scriptPubKey para um output P2SH seria:
OP_HASH160 <redeemScript hash> OP_EQUAL
Não há novos operadores aqui. Mas, temos o <redeemScript hash> como um novo elemento. Como o nome sugere, é um hash do script que precisamos fornecer para resgatar fundos (chamado de redeemScript). O scriptSig mudará dependendo do redeemScript. Geralmente, no entanto, você perceberá que se trata de uma combinação de assinaturas e das chaves públicas anexadas, seguido pelo (obrigatório) redeemScript:
<signature> <public key> <redeemScript>
Nossa avaliação é um pouco diferente da execução de stack que vimos até agora. São duas etapas. A primeira simplesmente verifica se você forneceu o hash correto.
Você verá que não fazemos nada com os elementos que precedem o redeemScript. Eles não são usados neste momento. Chegamos ao final deste miniprograma e o elemento do topo é diferente de zero. Isso significa que ele é válido.
Mas ainda não terminamos. Os
nodes da rede reconhecem esta estrutura como P2SH, então os elementos do scriptSig estão em modeo de espera, em outro stack. É neste momento que a assinatura e a chave pública serão utilizadas.
Até agora, tratamos o redeemScript como um elemento. Mas agora, o interpretaremos como instruções. Vamos usar o exemplo de um script de bloqueio P2PKH, para o qual devemos fornecer <signature> e <public key> que correspondem a um <public key hash> dentro do <redeemScript>.
Depois que o redeemScript for expandido, podemos ver que temos uma situação exatamente igual a uma transação P2PKH normal. A partir deste ponto, realizamos o procedimento usual.
Demonstramos aqui o que é chamado de script P2SH(P2PKH), mas é improvável que você encontre um desses por aí. Nada o impede de fazer um, mas ele não oferece benefícios adicionais e acaba ocupando mais espaço em um bloco (e portanto, custa mais).
Geralmente, o P2SH é útil para coisas como transações compatíveis com
multisignature ou
SegWit . Muitas vezes, transações multisig são muito grandes, pois podem exigir várias chaves. Antes da implementação do Pay-to-Script-Hash, o remetente precisava listar todas as chaves públicas possíveis em seu script de bloqueio.
Mas com o P2SH, não importa quão complexas sejam as condições de gasto. O hash do redeemScript terá sempre um tamanho fixo. Os custos são, portanto, repassados ao(s) usuário(s) que desejam desbloquear o script de bloqueio.
A compatibilidade SegWit é outro caso em que o P2SH é útil (veremos na próxima seção, detalhes sobre a estrutura da transação). O SegWit foi um soft fork que resultou em uma mudança nos formatos de bloco/transação. Por ser um opt-in upgrade (atualização opcional), nem todo software de carteira reconhece essas alterações.
Isso não importa se os clientes agruparem o hash de script SegWit no P2SH. Como acontece com todas as transações deste tipo, não é necessário saber qual é o redeemScript de desbloqueio.
Para entender o formato da transação no SegWit, você só precisa saber que não temos mais somente um scriptSig e um scriptPubKey. Agora, temos também um novo campo chamado witness (testemunha). Os dados utilizados para manter o scriptSig são movidos para o witness, portanto, o scriptSig fica vazio.
Caso você encontre endereços que começam com ‘bc1’, são o que chamamos de SegWit-nativos (diferente dos simplesmente compatíveis com SegWit, que começam com ‘3’, pois são endereços P2SH).
Pay-to-Witness-Pubkey-Hash (P2WPKH)
Pay-to-Witness-Pubkey-Hash (P2WPKH) é a versão SegWit do P2PKH. O witness será algo como:
<signature> <public key>
Você perceberá que este é igual ao scriptSig do P2PKH. Aqui, o scriptSig está vazio. Já o scriptPubKey será o seguinte:
<OP_0> <public key hash>
Parece um pouco estranho, não é? Onde estão os opcodes para permitir a comparação da assinatura, da chave pública e do hash?
Não estamos mostrando operadores adicionais aqui, pois os nodes que recebem a transação sabem o que fazer com ela, com base no comprimento do <public key hash>. Eles calcularão o comprimento e entenderão que a execução deve ser feita da mesma forma que a boa e velha transação P2PKH.
Nodes não atualizados não sabem como interpretar a transação dessa maneira, mas isso não importa. Sob as regras antigas, não há witness, então é feita a leitura de um scriptSig vazio e de outros dados. Os nodes avaliam essas informações e marcam como válidas – para os nodes, qualquer um pode gastar o output. É por isso que o SegWit é considerado um
soft fork compatível com versões anteriores.
Pay-to-Witness-Script-Hash (P2WSH)
Pay-to-Witness-Script Hash (P2WSH) é o novo P2SH. Se você chegou até aqui, provavelmente é capaz de imaginar como será seu formato, mas iremos examiná-lo de qualquer forma. Nosso witness é o que normalmente colocamos no scriptSig. Um exemplo de P2WSH que envolve uma transação P2PKH seria:
<signature 1> <public key>
Aqui está nosso scriptPubKey:
<OP_0> <script hash>
As mesmas regras estão em vigor. Os nodes SegWit fazem a leitura do comprimento do hash do script e determinam que será um output P2WSH, avaliado de forma semelhante ao P2SH. Enquanto isso, os nodes antigos consideram que se trata, simplesmente, de um output que qualquer um pode gastar.
Neste artigo, aprendemos um pouco sobre a construção de blocos de Bitcoin. Vamos fazer um breve resumo:
Tipo de Script | Descrição |
---|
Pay-to-Pubkey (P2PK) | Bloqueia fundos vinculados a uma determinada chave pública |
Pay-to-Pubkey-Hash (P2PKH) | Bloqueia fundos vinculados a um determinado hash de chave pública (como um endereço) |
Pay-to-Script-Hash (P2SH) | Bloqueia fundos vinculados ao hash de um script que o destinatário pode fornecer |
Pay-to-Witness-Pubkey-Hash (P2WPKH) | Versão SegWit do P2PK |
Pay-to-Witness-Script-Hash (P2WSH) | Versão SegWit do P2SH |
Depois de aprender mais sobre o sistema Bitcoin, você começa a entender por que ele tem tanto potencial. As transações podem ser feitas por muitos componentes diferentes. Ao manipular esses blocos de construção, os usuários têm uma flexibilidade muito grande quando se trata de definir as condições sobre como e quando os fundos podem ser gastos.