Taproot

A grande atualização de 2021

BIP 341: Taproot: Regras de gasto SegWit versão 1

BIP 342: Validação de Scripts Taproot

Diagrama mostrando um resumo de como o taproot funciona no Bitcoin

Taproot é uma grande atualização do Bitcoin, ativada em 2021.

A mudança principal foi a introdução do script de travamento Pay To Taproot (P2TR), que permite destravar uma saída usando uma escolha de scripts personalizados. Houve também duas mudanças extras importantes:

  1. Assinaturas Schnorr — O novo script de travamento P2TR usa o esquema de assinatura Schnorr, mais eficiente que o ECDSA.
  2. Tapscript — Os scripts personalizados dentro do P2TR usam uma versão ligeiramente modificada do Script chamada Tapscript.

Então a atualização Taproot apenas introduziu um novo e útil mecanismo de travamento para saídas, que usa Assinaturas Schnorr e Tapscript. No geral, foi uma atualização técnica razoavelmente grande, tornando-se a maior atualização do Bitcoin desde o Segregated Witness (2017).

Esta página foca em como construir e gastar o novo script de travamento P2TR.

Fundamentos

Como o Taproot funciona?

Diagrama mostrando os fundamentos de um gasto por caminho de chave e por caminho de script em um script de travamento P2TR

Um script de travamento P2TR pode ser destravado de uma de duas formas:

  1. Caminho de chave (key path). Você fornece uma assinatura para a chave pública usada para criar o script de travamento. Funciona de forma similar a um P2WPKH simples.
  2. Caminho de script (script path). Você usa um dos múltiplos scripts personalizados para destravar a saída. É como usar um P2WSH, mas com vários scripts para escolher.

Todos os scripts personalizados são organizados em uma árvore. Essa árvore de scripts é usada para criar uma raiz de Merkle, que é uma impressão digital de todas as formas de destravar a saída. Essa impressão digital (tweak) é então combinada com a chave pública para criar uma chave pública ajustada.

Esta chave pública ajustada é o que vai no script de travamento.

Você pode então destravar de duas formas:

  1. Pela chave pública, ajustando sua chave privada para criar uma assinatura para a chave pública ajustada. Isto é um gasto por caminho de chave.
  2. Usando um dos scripts personalizados da árvore, fornecendo um caminho de Merkle para provar que seu script estava na árvore. Isto é um gasto por caminho de script.

Em suma, ambas as opções de gasto estão embutidas em uma única chave pública ajustada (chamada de taproot).

A beleza do P2TR é que a chave pública ajustada pode ser destravada com uma única assinatura. Esta assinatura é criada ajustando a chave privada original pelo mesmo valor do tweak — o que funciona graças à matemática das curvas elípticas.

O nome oficial para a chave pública ajustada é o taproot. Mas, para deixar as explicações a seguir mais claras, normalmente vou me referir a ela apenas como chave pública ajustada daqui em diante.

Benefícios

Quais são os benefícios do Taproot?

O design do script de travamento Pay To Taproot (P2TR) oferece alguns benefícios.

Em suma, o P2TR oferece flexibilidade para colocar várias condições de gasto em uma saída de forma privada e eficiente.

1. Flexibilidade

O P2TR permite colocar múltiplas condições de gasto (scripts personalizados) em uma única saída.

Pense no caminho de chave como o método "padrão", e no caminho de script como alternativas caso o padrão não seja possível:

Quase sempre scripts interessantes têm um ramo lógico no topo que permite satisfazer o contrato com nada mais que uma assinatura de todas as partes. Outros ramos seriam usados apenas quando algum participante falha em cooperar.
Gregory Maxwell, Lista de Discussão bitcoin-dev (2018)

2. Privacidade

Graças à estrutura de árvore de Merkle, apenas o script usado é revelado ao gastar pelo caminho de script.

Você pode construir uma árvore com centenas de scripts diferentes, mas apenas o que você usar será revelado — os demais aparecem apenas como hashes dos scripts originais.

Isso significa que todas as outras condições de gasto possíveis de um P2TR são mantidas em sigilo.

Se você gasta um P2TR pelo caminho de chave, nenhum dos scripts personalizados é revelado. Ninguém saberia se havia condições de gasto adicionais.

3. Eficiência

Você pode pensar que incluir múltiplas condições de gasto diferentes em uma saída criaria um script de travamento grande e caro.

Porém, graças à estrutura de árvore de Merkle usada para todos os scripts personalizados, todas as condições de gasto são reduzidas a uma única impressão digital de 32 bytes (raiz de Merkle). Você só precisa revelar uma pequena porção desta árvore (caminho de Merkle) ao destravar a saída usando o script escolhido.

Além disso, graças à matemática das curvas elípticas (adição de pontos), podemos mesclar esta raiz de Merkle com uma chave pública para criar uma chave pública ajustada. Esta chave pública ajustada permite que a saída seja gasta usando uma assinatura ou usando um dos scripts personalizados que compõem a raiz de Merkle.

Como resultado, todas as condições de gasto possíveis podem ser embutidas em uma única "chave pública ajustada" de 32 bytes (o taproot), o que é mais eficiente do que colocar múltiplos scripts personalizados diretamente no script de travamento.

Um script de travamento P2TR tem apenas 34 bytes — o mesmo tamanho que um P2WSH (que contém apenas uma condição de gasto).

Com muitos scripts personalizados, o caminho de Merkle será maior, aumentando os dados necessários para destravar um P2TR.

4. Atualizabilidade

O P2TR foi projetado para permitir upgrades futuros. É por isso que você vê campos aparentemente redundantes (ex.: versão da folha, anexo, flag de extensão, sighash epoch) incluídos no gasto de um P2TR.

Construção

Como criar um P2TR?

Diagrama mostrando a construção de um ScriptPubKey P2TR

Construir um P2TR é relativamente simples. Você cria um ScriptPubKey com o seguinte padrão:

OP_1 OP_PUSHBYTES_32 <chave pública ajustada de 32 bytes>

A chave pública ajustada é criada combinando uma chave pública com a raiz de Merkle de uma árvore de scripts personalizados.

A parte mais complexa é organizar todos os scripts personalizados em uma estrutura de árvore e calcular a raiz de Merkle a partir dela. No entanto, essa árvore de scripts pode ser tão simples ou tão complexa quanto você quiser (ou você pode simplesmente não usar uma árvore de scripts).

Abaixo está uma visão geral das etapas envolvidas na criação de um script de travamento P2TR.

Veja os exemplos para um guia técnico passo a passo com dados reais.

1. Chave Pública

também Chave Pública Interna, Taproot Internal Key

O primeiro passo é gerar uma chave pública.

Esta chave pública é usada para o caminho de chave, que será o método "padrão" para destravar a saída P2TR. Ela é gerada da mesma forma que qualquer par de chave privada/chave pública usado no Bitcoin.

No entanto, P2TR usa chaves públicas de 32 bytes em vez de 33 bytes (como as chaves públicas comprimidas). Isso porque scripts de travamento P2TR usam assinaturas Schnorr, e chaves públicas neste esquema contêm apenas a coordenada x de 32 bytes.

Exemplo

chave pública = a2fc329a085d8cfc4fa28795993d7b666cee024e94c40115141b8e9be4a29fa4

Chave Pública

Calcule a chave pública a partir de uma chave privada.

0 bytes

Chave Pública

Coordenadas
0d
0d

Uma chave pública é apenas um ponto em uma curva elíptica. A chave pública final são essas coordenadas em hexadecimal.

Compressão

A curva é simétrica no eixo x, então a chave comprimida guarda só a coordenada x e se o y é par ou ímpar. A x-only é usada em saídas Taproot.

0 bytes

Nunca digite sua chave privada em um site, nem use uma chave privada gerada por um site. Sites podem facilmente salvar a chave privada e usá-la para roubar seus bitcoins.

Você pode converter uma chave pública comprimida padrão de 33 bytes para 32 bytes simplesmente removendo o primeiro byte.

Uma chave pública é obrigatória para construir um P2TR.

2. Árvore de Scripts

A árvore de scripts contém todos os scripts personalizados que você deseja poder usar para destravar a saída P2TR (além do gasto por chave pública).

Você pode usar quantos scripts quiser* e organizar a árvore como preferir.

*Limitado a 2128 scripts, devido ao tamanho máximo do caminho de Merkle no bloco de controle.

A árvore de scripts e sua raiz de Merkle são construídas em 3 passos:

Uma árvore de scripts não é obrigatória para construir um P2TR.

1. Folhas

As folhas da árvore são feitas dos scripts personalizados que você deseja usar.

Cada folha contém dois dados:

  1. Versão — Indica a versão do script que vem a seguir, permitindo mudanças futuras na linguagem de script.
    192 (0xc0) = tapscript (padrão atual)
  2. Script — Seu script de travamento personalizado (tapscript).

Exemplo

versão da folha = c0
script da folha  = 5187

Script

Decodifique e codifique um script.

0 bytes

  • Você não precisa construir folhas para sua árvore. Sem árvore, o P2TR será apenas caminho de chave.
  • Scripts-folha usam atualmente versão 192 (0xc0), que indica tapscript.
Por que a versão da folha é 192 (0xc0)?

A versão da folha é codificada no primeiro byte do bloco de controle, então é ideal que este byte não conflite com outros elementos no testemunha.

Restrições:

  • Não pode ser ímpar — o bit menos significativo do byte de controle é reservado para a paridade da chave.
  • Não pode ser 80 (0x50) — 0x50 é usado como primeiro byte do anexo.
  • Deve evitar ser confundido com o primeiro byte de um script válido.

Os bytes disponíveis para versões de folha distintas são: 0xc0 a 0xfe, 0x66, 0x7e, 0x80, 0x84, 0x96, 0x98, 0xba, 0xbc, 0xbe.

Começar em 0xc0 (192) ajuda a evitar confusão com outros elementos no testemunha.

2. Organização

Diagrama mostrando uma árvore de scripts balanceada onde cada script tem igual probabilidade de execução Diagrama mostrando uma árvore de scripts desbalanceada onde alguns scripts são mais prováveis de serem executados

A única consideração na organização é que você quer o script mais provável de ser executado mais próximo da raiz, pois o caminho de Merkle será mais curto, economizando em taxas.

Ao construir uma [árvore de scripts], se o usuário acredita que alguns ramos são mais prováveis de serem executados, pode colocá-los mais próximos da Raiz do Script. Isso economizará espaço no testemunha quando os ramos preferidos forem executados.
BIP 114: Merkelized Abstract Syntax Tree

Não se preocupe demais com a organização se você não tiver certeza. Apenas lhe dá a oportunidade de economizar um pouco em taxas.

3. Raiz de Merkle

O passo final é aplicar hash em todas as folhas para produzir uma única raiz de Merkle, calculada em 3 etapas:

1. Hash da Folha

Crie um hash com tag para cada folha:

Exemplo

versão da folha      = c0
tamanho(script) = 02
script da folha       = 5187

dados = c0025187

hash da folha = tagged_hash("TapLeaf", dados)
hash da folha = 6b13becdaf0eee497e2f304adcfa1c0c9e84561c9989b7f2b5fc39f5f90a60f6
Ícone Ferramenta Script

Script

Decodifique e codifique um script.

0 bytes

Ícone Ferramenta Compact Size

Compact Size

Converta entre um número e sua codificação compact size (o varint usado no Bitcoin).

0d
0 bytes
Prefixo

O primeiro byte indica quais bytes codificam o inteiro:

Nota: os bytes que codificam o inteiro estão em little endian.

Ícone Ferramenta Tagged Hash

Tagged Hash

Cria um hash de dados com um prefixo de tag.
Usado em Schnorr sign, Schnorr verify e Taproot.

0 bytes

SHA256(SHA256(string) || SHA256(string) || dados)

0 bytes

O formato tamanho(script) + script é chamado de "script serializado". Este formato (isto é, prefixar o script com um campo compact size) também é usado ao aplicar hash em scriptpubkeys dentro do algoritmo de assinatura. Isso é fácil de esquecer e provavelmente vai te confundir na primeira vez.

2. Hash de Ramo

Após calcular cada hash de folha, você calcula os ramos subsequentes da sua árvore.

Cada hash de ramo é um hash com tag de pares de hashes de folha/ramo anteriores:

Dependendo de onde você está na árvore, o próximo ramo será calculado usando hashes de folha ou hashes de ramo anteriores (ou uma combinação de ambos).

É importante notar que o hash menor vem primeiro. Isso é para que a raiz de Merkle possa ser recalculada depois sem precisar fornecer informações sobre a ordem dos hashes.

Exemplo

┌────────┐ ┌────────┐
│ folha 1│ │ folha 2│
└───┬────┘ └───┬────┘
    └────┬─────┘
     ┌───┴────┐ ┌────────┐
     │ ramo 1 │ │ folha 3│
     └───┬────┘ └─────┬──┘
         └─────┬──────┘
           ┌───┴────┐ ┌────────┐
           │ ramo 2 │ │ folha 4│
           └───┬────┘ └────┬───┘
               └────┬──────┘
                ┌───┴────┐ ┌────────┐
                │ ramo 3 │ │ folha 5│
                └───┬────┘ └────┬───┘
                    └─────┬─────┘
                      ┌───┴────┐
                      │ ramo 4 │
                      └────────┘

versão da folha 1     = c0
tamanho(script folha 1) = 02
script da folha 1     = 5187
hash da folha 1       = 6b13becdaf0eee497e2f304adcfa1c0c9e84561c9989b7f2b5fc39f5f90a60f6

versão da folha 2     = c0
tamanho(script folha 2) = 02
script da folha 2     = 5287
hash da folha 2       = ed5af8352e2a54cce8d3ea326beb7907efa850bdfe3711cef9060c7bb5bcf59e

versão da folha 3     = c0
tamanho(script folha 3) = 02
script da folha 3     = 5387
hash da folha 3       = 160bd30406f8d5333be044e6d2d14624470495da8a3f91242ce338599b233931

versão da folha 4     = c0
tamanho(script folha 4) = 02
script da folha 4     = 5487
hash da folha 4       = bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d

versão da folha 5     = c0
tamanho(script folha 5) = 02
script da folha 5     = 5587
hash da folha 5       = 54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f

ramo 1 (hash folha 1 + hash folha 2) = 1324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9
ramo 2 (ramo 1 + hash folha 3)       = beec0122bddd26f642140bcd922e0264ce1e2be5808a41ae58d82e829bc913d7
ramo 3 (ramo 2 + hash folha 4)       = a4e0d9cc12ce2f32069e98247581d5eb9ca0a4cf175771a8df2c53a93dcb0ebd
ramo 4 (hash folha 5 + ramo 3)       = b5b72eea07b3e338962944a752a98772bbe1f1b6550e6fb6ab8c6e6adb152e7c

NOTA: No ramo 4, o hash da folha 5 vem primeiro porque é menor que o hash do ramo 3.
3. Hash Raiz

Continue aplicando hash recursivamente até obter um único hash raiz — a raiz de Merkle da sua árvore de scripts.

Exemplo

┌────────┐ ┌────────┐
│ folha 1│ │ folha 2│
└───┬────┘ └───┬────┘
    └────┬─────┘
     ┌───┴────┐ ┌────────┐
     │ ramo 1 │ │ folha 3│
     └───┬────┘ └─────┬──┘
         └─────┬──────┘
           ┌───┴────┐ ┌────────┐
           │ ramo 2 │ │ folha 4│
           └───┬────┘ └────┬───┘
               └────┬──────┘
                ┌───┴────┐ ┌────────┐
                │ ramo 3 │ │ folha 5│
                └───┬────┘ └────┬───┘
                    └─────┬─────┘
                      ┌───┴────┐
                      │ ramo 4 │ <- hash raiz (também chamado de raiz de Merkle)
                      └────────┘

ramo 1 (folha 1 + folha 2)   = 1324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9
ramo 2 (ramo 1 + folha 3)    = beec0122bddd26f642140bcd922e0264ce1e2be5808a41ae58d82e829bc913d7
ramo 3 (ramo 2 + folha 4)    = a4e0d9cc12ce2f32069e98247581d5eb9ca0a4cf175771a8df2c53a93dcb0ebd
ramo 4 (folha 5 + ramo 3)    = b5b72eea07b3e338962944a752a98772bbe1f1b6550e6fb6ab8c6e6adb152e7c

raiz de Merkle = ramo 4
raiz de Merkle = b5b72eea07b3e338962944a752a98772bbe1f1b6550e6fb6ab8c6e6adb152e7c
  • Se você tem apenas uma folha, a raiz de Merkle será o hash dessa folha.
  • Se você não usa árvore de scripts, a raiz será vazia (zero bytes).

3. Taproot

também chave pública ajustada, Taproot Output Key

Depois de selecionar sua chave pública e calcular a raiz de Merkle da sua árvore de scripts, agora você combina ambas para criar uma nova chave pública ajustada (taproot) para colocar no script de travamento P2TR final.

Esta chave pública ajustada é calculada em 3 etapas:

1. Tweak

O tweak é um hash com tag da chave pública e da raiz de Merkle:

Exemplo

chave pública  = a2fc329a085d8cfc4fa28795993d7b666cee024e94c40115141b8e9be4a29fa4
raiz de Merkle = b5b72eea07b3e338962944a752a98772bbe1f1b6550e6fb6ab8c6e6adb152e7c

tweak = tagged_hash("TapTweak", chave pública + raiz de Merkle)
tweak = bf0094eae70ba67e2f9fc3c4b81f078c90931855a8d24c959619174c92060cde
Ícone Ferramenta Tagged Hash (TapTweak)

Tagged Hash

Cria um hash de dados com um prefixo de tag.
Usado em Schnorr sign, Schnorr verify e Taproot.

0 bytes

SHA256(SHA256(string) || SHA256(string) || dados)

0 bytes

Se você não está usando uma árvore de scripts, os dados serão apenas a chave pública.

2. Chave Pública Ajustada

A chave pública ajustada é calculada adicionando o ponto da chave pública a um novo ponto calculado a partir do tweak:

  1. Multiplique o ponto gerador pelo tweak para criar um "ponto de tweak".
  2. Adicione este "ponto de tweak" ao ponto da chave pública original para criar a chave pública ajustada.

Exemplo

tweak = 0xbf0094eae70ba67e2f9fc3c4b81f078c90931855a8d24c959619174c92060cde

ponto de tweak = ponto gerador * tweak
ponto de tweak = { x: 95269..., y: 29242... }

chave pública = a2fc329a085d8cfc4fa28795993d7b666cee024e94c40115141b8e9be4a29fa4
ponto da chave pública = { x: 73720..., y: 11058... }

chave pública ajustada = tweak + chave pública
chave pública ajustada = { x: 38964..., y: 63284... }

chave pública ajustada = 562529047f476b9a833a5a780a75845ec32980330d76d1ac9f351dc76bce5d72 (x)
Ícone Ferramenta EC Multiply

Multiplicação de Ponto (EC Multiply)

Multiplique um ponto na curva elíptica secp256k1.

Ponto 1
x:
0d
y:
0d
0d
Ponto 1 × Multiplicador
x:
0d
y:
0d
Ícone Ferramenta EC Add

Soma de Pontos (EC Add)

Some dois pontos na curva elíptica secp256k1.

Ponto 1
x:
0d
y:
0d
Ponto 2
x:
0d
y:
0d
Ponto 1 + Ponto 2
x:
0d
y:
0d

Esta chave pública ajustada será um ponto completamente diferente na curva elíptica comparado ao ponto da chave pública original.

Graças à matemática das curvas elípticas, se ajustarmos a chave privada pelo mesmo valor, ela corresponderá a esta chave pública ajustada.

4. ScriptPubKey

Finalmente, colocamos a chave pública ajustada em um script de travamento para criar uma saída P2TR:

OP_1 OP_PUSHBYTES_32 <chave pública ajustada de 32 bytes>

Exemplo

ASM

OP_1
OP_PUSHBYTES_32
562529047f476b9a833a5a780a75845ec32980330d76d1ac9f351dc76bce5d72

Hex

5120562529047f476b9a833a5a780a75845ec32980330d76d1ac9f351dc76bce5d72

Transação: 0c27dceb3c5e8aeffaad5c01229eac985b4a3d56388fcef07daecb1b5482495e (Saída 0) (usada no key path spend)

Transação: 88047644c7e42421861b5d15551aa29151f86d81409fd9a3831f43a541505720 (Saída 1) (usada no script path spend)

Ícone Ferramenta Script

Script

Decodifique e codifique um script.

0 bytes

  • Um ScriptPubKey P2TR tem sempre 34 bytes.
  • P2TR usa OP_1, enquanto P2WPKH e P2WSH usam OP_0.
  • Este padrão de ScriptPubKey é similar aos scripts de travamento P2WPKH e P2WSH introduzidos na atualização Segregated Witness.

Endereço

Um ScriptPubKey P2TR pode ser convertido para um endereço usando Bech32m.

Exemplo

scriptpubkey (hex) = 5120562529047f476b9a833a5a780a75845ec32980330d76d1ac9f351dc76bce5d72
endereço            = bc1p2cjjjprlga4e4qe6tfuq5avytmpjnqpnp4mdrtylx5wuw67wt4eqg9jscq

Endereço (Bech32)

Codifique um script de travamento P2WPKH, P2WSH ou P2TR em um endereço.

ScriptPubKey
Versão
0 bytes
0 bytes Tipo:
Rede

Codificação Bech32 do ScriptPubKey

0 caracteres

    Um endereço P2TR tem sempre 62 caracteres e começa com bc1p (mainnet).

    Gasto

    Como gastar um P2TR?

    Há dois métodos para gastar uma saída P2TR:

    1. Key Path Spend — Gasto via chave pública.
    2. Script Path Spend — Gasto usando um dos scripts-folha da árvore de scripts.

    O código de destravamento de ambos os métodos é colocado no testemunha. A estrutura do campo de testemunha varia dependendo de você usar o key path spend ou o script path spend.

    1. Key Path Spend

    Um key path spend P2TR é onde você destrava a saída via a chave pública padrão.

    Este gasto é relativamente simples, pois você só precisa fornecer uma assinatura que corresponda à chave pública ajustada no ScriptPubKey.

    Diagrama mostrando como gastar uma saída P2TR usando o método de gasto por caminho de chave

    Um key path spend é realizado em 3 etapas:

    1. Chave Privada Ajustada

    Para conseguir criar uma assinatura que corresponda a esta chave pública ajustada, primeiro você precisa ajustar a chave privada original que foi usada para criar a chave pública original.

    A chave privada ajustada é calculada simplesmente adicionando o tweak à chave privada original:

    Exemplo

    chave privada        = ce1fc7baa9db31c4ef9c6564f70d551f41fc479bb23fa844d50848220edaaf91
    n                    = fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
    chave privada negada = n - chave privada
    chave privada negada = 31e038455624ce3b10639a9b08f2aadf78b2954afd08f7f6eaca166ac15b91b0
    
    tweak                = bf0094eae70ba67e2f9fc3c4b81f078c90931855a8d24c959619174c92060cde
    
    chave privada ajustada = (chave privada negada + tweak) % n
    chave privada ajustada = f0e0cd303d3074b940035e5fc111b26c0945ada0a5db448c80e32db753619e8e
    • Se a chave privada original produz uma chave pública com a coordenada y ímpar, você precisa negar a chave privada primeiro, de modo que ela produza a chave pública equivalente, mas com coordenada y par. Isto é feito subtraindo a chave privada do número de pontos na curva (n). O motivo é que as chaves públicas no esquema de assinatura Schnorr no Bitcoin usam apenas coordenadas y pares.
    • A adição é feita módulo n para manter o resultado dentro da faixa válida de chaves privadas.

    Graças à matemática das curvas elípticas, esta chave privada ajustada corresponderá de fato à chave pública ajustada.

    Não perca o lado da árvore de scripts do P2TR. Você precisará da raiz de Merkle para calcular a chave privada ajustada.

    2. Hash de Assinatura

    A mensagem a ser assinada é chamada de hash de assinatura, construída a partir dos dados da transação. Veja o algoritmo de assinatura taproot para detalhes.

    Exemplo

    transação (não assinada) = 020000000001015e4982541bcbae7df0ce8f38563d4a5b98ac9e22015cadfaef8a5e3cebdc270c0000000000ffffffff014c1d0000000000001600142cfec11b4e6d86607fc072804f0c7cc5dad213d00000000000
    
    hash type byte = 01
    annex present  = 0
    extension flag = 0
    
    version            = 02000000
    locktime           = 00000000
    hash prevouts      = fcb2a500a85bcc659833d70ab67aca05e96403a334455c7c17a9f8e88fc56c43
    hash amounts       = 8e965763e6a4bbc1088a94bf6c9cb3cbbdb4955f88355c807362a0fd43de4e3a
    hash scriptpubkeys = 84fbd6ff94eb947a0cb9de10adb3485927dcfff68fce92bbc955327a27fbf166
    hash sequences     = ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e
    hash outputs       = c8db30b8a8c45860a0670e3036487888d35c21230f2fc37b0243d11ecf8d934f
    spend type         = 00
    input index        = 00000000
    
    mensagem de assinatura =  hash type byte + version + locktime + hash prevouts + hash amounts + hash scriptpubkeys + hash sequences + hash outputs + spend type + input index
    mensagem de assinatura = 010200000000000000fcb2a500a85bcc659833d70ab67aca05e96403a334455c7c17a9f8e88fc56c438e965763e6a4bbc1088a94bf6c9cb3cbbdb4955f88355c807362a0fd43de4e3a84fbd6ff94eb947a0cb9de10adb3485927dcfff68fce92bbc955327a27fbf166ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0ec8db30b8a8c45860a0670e3036487888d35c21230f2fc37b0243d11ecf8d934f0000000000
    
    sighash epoch  = 00
    extensão       =
    
    hash de assinatura = tagged_hash("TapSighash", sighash epoch + mensagem de assinatura + extensão)
    hash de assinatura = be2cbfeb3f4e9966451179c13658ef059268f58ddda6c3708bdd51719a004d2c

    O sighash epoch atualmente é 00. Não se esqueça de incluí-lo antes da mensagem de assinatura ao criar o hash de assinatura.

    O algoritmo de assinatura taproot é similar ao algoritmo de assinatura SegWit, mas com algumas pequenas diferenças.

    Para key path spends: extension flag = 0. Isto é usado durante a etapa do algoritmo de assinatura.

    3. Assinatura

    A assinatura é criada assinando o hash de assinatura com a chave privada ajustada usando o esquema Schnorr.

    Exemplo

    chave privada ajustada = f0e0cd303d3074b940035e5fc111b26c0945ada0a5db448c80e32db753619e8e
    aux rand                = 0000000000000000000000000000000000000000000000000000000000000000
    mensagem                = be2cbfeb3f4e9966451179c13658ef059268f58ddda6c3708bdd51719a004d2c
    
    assinatura = schnorr_sign(chave privada ajustada, mensagem, aux rand)
    assinatura = 095282ddced815fc7130a4b78b8000b14b0205614f92958d89529afeedef26712dd452731d1721599b4e902b98017c970e51827ed287f6171234e054a61275a4
    
    assinatura + hash type byte = 095282ddced815fc7130a4b78b8000b14b0205614f92958d89529afeedef26712dd452731d1721599b4e902b98017c970e51827ed287f6171234e054a61275a401
    Ícone Ferramenta Schnorr Sign

    Assinatura Schnorr

    Assine uma mensagem usando uma chave privada.

    0x
    0 bytes
    0x
    0 bytes
    0x
    0 bytes
    0x
    0 bytes

    Nunca insira sua chave privada em um site, nem use uma chave privada gerada por um site. Sites podem facilmente salvar a chave privada e usá-la para roubar seus bitcoins.

    Testemunha

    Por fim, depois de criar a assinatura, basta construir o campo de testemunha para destravar a saída P2TR.

    Para um key path spend, o testemunha contém uma única assinatura Schnorr:

    <assinatura de 64/65 bytes>

    Exemplo

    ASM

    095282ddced815fc7130a4b78b8000b14b0205614f92958d89529afeedef26712dd452731d1721599b4e902b98017c970e51827ed287f6171234e054a61275a401

    Separado

    {
      "stackitems": "01",
      "0": {
        "size": "41",
        "item": "095282ddced815fc7130a4b78b8000b14b0205614f92958d89529afeedef26712dd452731d1721599b4e902b98017c970e51827ed287f6171234e054a61275a401"
      }
    }

    Hex

    0141095282ddced815fc7130a4b78b8000b14b0205614f92958d89529afeedef26712dd452731d1721599b4e902b98017c970e51827ed287f6171234e054a61275a401

    Transação: 6f43140e371f071dafe11a73fcda9ae455f1dd9be93f2b7cc4cde21737c759a5 (Entrada 0)

    Todas as saídas P2TR usam o algoritmo de assinatura Schnorr.

    • O fato do testemunha conter 1 elemento é o que identifica um key path spend.
    • Assinaturas em P2TR podem ter 64 ou 65 bytes. O byte de hash type final é opcional (o SIGHASH_DEFAULT é usado se ausente).
    Anexo (Annex)
    Diagrama mostrando a localização do anexo no testemunha de um gasto por caminho de chave e de um gasto por caminho de script
    Localização do anexo no testemunha de um gasto por caminho de chave e de um gasto por caminho de script.

    O anexo é um elemento de dados opcional incluído no final do testemunha.

    É identificado por um byte inicial 0x50 e é sempre o último elemento do testemunha, tanto em gastos por caminho de chave quanto em gastos por caminho de script. Este dado é reservado para upgrades futuros.

    O anexo é removido do testemunha antes da validação do script.

    O anexo não é usado atualmente. Não deve ser incluído ao gastar um P2TR.

    Defina annex present = 1 na etapa do algoritmo de assinatura se você estiver incluindo um anexo no seu testemunha.

    2. Script Path Spend

    Esta é a parte divertida.

    Diagrama mostrando como gastar uma saída P2TR usando o método de gasto por caminho de script
    Como gastar uma saída P2TR usando o método de gasto por caminho de script.

    Um script path spend requer 3 coisas:

    1. Um script-folha da árvore de scripts.
    2. As entradas necessárias para destravar aquele script-folha.
    3. Um caminho de Merkle provando que o script-folha fazia parte da árvore.

    Cada um destes 3 componentes é então colocado no testemunha. Veja como construir cada um:

    1. Entradas do Script

    São os itens da pilha necessários para destravar o script-folha escolhido.

    Estes itens da pilha são equivalentes aos elementos de dados que você usaria para destravar qualquer script de travamento P2WSH personalizado. Estas entradas de script podem ter um número variável de elementos; você só precisa fornecer os dados relevantes necessários para destravar o script-folha.

    Exemplo

    entradas do script = 03 (para destravar "OP_EQUAL OP_3")

    Script Path Spend — Algoritmo de Assinatura

    Se suas entradas de script incluem uma assinatura, o algoritmo de assinatura será ligeiramente diferente comparado a um key path spend.

    As 2 diferenças-chave são:

    1. Você precisa usar extension flag = 1.
    2. Você precisa construir uma extensão para incluir alguns dados extras no final da mensagem de assinatura comum.

    Não é uma grande diferença, mas é algo que provavelmente vai te pegar de surpresa na primeira vez que você tentar fazer um script path spend que requer uma assinatura.

    2. Script-Folha

    O script-folha completo, que será executado em conjunto com as entradas anteriores para destravar o P2TR.

    Exemplo

    script-folha = 5387 (folha 3 — OP_EQUAL OP_3)

    A parte entradas do script + script-folha do testemunha de um script path spend P2TR tem a mesma estrutura que o testemunha de um P2WSH.

    3. Bloco de Controle

    O último elemento no testemunha. Contém o caminho de Merkle que prova que o script-folha fazia parte da árvore, e a chave pública original.

    Exemplo

    byte de controle = c1
    chave pública     = a2fc329a085d8cfc4fa28795993d7b666cee024e94c40115141b8e9be4a29fa4
    caminho de Merkle = 1324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f
    
    bloco de controle = c1a2fc329a085d8cfc4fa28795993d7b666cee024e94c40115141b8e9be4a29fa41324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9...

    O bloco de controle completo é dividido em 3 seções:

    1. Byte de Controle

    Codifica a versão da folha e um bit indicando a paridade da coordenada y da chave pública ajustada:

    Para criar o byte de controle: some a versão da folha com o bit de paridade.

    Exemplo

    versão da folha = c0 (versão do script-folha que estamos usando, isto é, a folha 3)
    bit de paridade = 1  (a coordenada y da chave pública ajustada é ímpar)
    
    byte de controle = versão da folha + bit de paridade
    byte de controle = c1
    • A paridade é necessária para permitir verificação em lote.
    • O último bit é reservado para a paridade, então versões de folha são sempre pares.
    2. Chave Pública

    Os próximos 32 bytes do bloco de controle contêm a chave pública original.

    Ela precisa ser revelada para que a chave pública ajustada possa ser calculada a partir dela.

    Exemplo

    chave pública = a2fc329a085d8cfc4fa28795993d7b666cee024e94c40115141b8e9be4a29fa4
    3. Caminho de Merkle

    Uma série de hashes de folha/ramo de 32 bytes necessários para reconstruir a raiz de Merkle. Os hashes de folha/ramo relevantes devem ser colocados em ordem, para que qualquer pessoa possa reconstruir a mesma raiz de Merkle.

    Este caminho de Merkle prova que o seu script-folha fazia parte da árvore de scripts original, pois a combinação da chave pública anterior com a raiz de Merkle resultante baterá com a chave pública ajustada colocada no ScriptPubKey.

    Exemplo

    ┌────────┐ ┌────────┐
    │ folha 1│ │ folha 2│
    └───┬────┘ └───┬────┘
        └────┬─────┘
         X───┴────┐ ┌────────┐
         │ ramo 1 │ │ folha 3│ <- gastando
         └───┬────┘ └─────┬──┘
             └─────┬──────┘
               ┌───┴────┐ X────────┐
               │ ramo 2 │ │ folha 4│
               └───┬────┘ └────┬───┘
                   └────┬──────┘
                    ┌───┴────┐ X────────┐
                    │ ramo 3 │ │ folha 5│
                    └───┬────┘ └────┬───┘
                        └─────┬─────┘
                          ┌───┴────┐
                          │ ramo 4 │
                          └────────┘   Nota: X = hashes necessários para construir o caminho de Merkle
    
    
    hash folha 1 = 6b13becdaf0eee497e2f304adcfa1c0c9e84561c9989b7f2b5fc39f5f90a60f6
    hash folha 2 = ed5af8352e2a54cce8d3ea326beb7907efa850bdfe3711cef9060c7bb5bcf59e
    hash folha 3 = 160bd30406f8d5333be044e6d2d14624470495da8a3f91242ce338599b233931
    hash folha 4 = bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d
    hash folha 5 = 54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f
    
    ramo 1       = 1324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9
    ramo 2       = beec0122bddd26f642140bcd922e0264ce1e2be5808a41ae58d82e829bc913d7
    ramo 3       = a4e0d9cc12ce2f32069e98247581d5eb9ca0a4cf175771a8df2c53a93dcb0ebd
    ramo 4       = b5b72eea07b3e338962944a752a98772bbe1f1b6550e6fb6ab8c6e6adb152e7c
    
    caminho de Merkle = ramo 1 + hash folha 4 + hash folha 5
    caminho de Merkle = 1324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f

    O caminho de Merkle contém o número mínimo de hashes para reconstruir a raiz. Quanto mais próximo seu script-folha estiver da raiz, mais curto será o caminho.

    O caminho de Merkle é limitado a 128 hashes.

    Testemunha

    O testemunha para um script path spend contém as entradas do script, o script-folha e o bloco de controle:

    <entradas do script...> <script-folha> <bloco de controle>

    Exemplo

    ASM

    03
    5387
    c1a2fc329a085d8cfc4fa28795993d7b666cee024e94c40115141b8e9be4a29fa41324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f

    Separado

    {
      "stackitems": "03",
      "0": { "size": "01", "item": "03" },
      "1": { "size": "02", "item": "5387" },
      "2": { "size": "81", "item": "c1a2fc329a085d8cfc4fa28795993d7b666cee024e94c40115141b8e9be4a29fa41324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f" }
    }

    Hex

    03010302538781c1a2fc329a085d8cfc4fa28795993d7b666cee024e94c40115141b8e9be4a29fa41324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f

    Transação: fa7eb13f6d854ed32ef284983c620f74050dd6d119dc9e91ad09c083b0267f8f (Entrada 1)

    O tamanho do testemunha de um script path spend varia dependendo de:

    1. O tamanho do script-folha.
    2. Quantas entradas de script são necessárias (isto é, a complexidade do script-folha).
    3. Quão acima da árvore o script-folha estava (isto é, quantos hashes de folha/ramo precisam ser fornecidos no bloco de controle).
    O fato do testemunha conter mais de 1 elemento é o que o identifica como script path spend.

    Algoritmo de Assinatura

    Como criar uma assinatura para um P2TR?

    Diagrama mostrando um resumo de como criar uma assinatura para uma saída P2TR usando o algoritmo de assinatura taproot
    Resumo de como criar uma assinatura para uma saída P2TR usando o algoritmo de assinatura taproot.

    Saídas P2TR usam um algoritmo de assinatura especial. Em vez de usar toda a transação bruta diretamente, você coleta partes específicas em uma mensagem de assinatura comum.

    Isso permite reutilizar partes da mensagem ao criar múltiplas assinaturas para a mesma transação.

    Este algoritmo de assinatura é similar ao algoritmo SegWit. Ele apenas tem alguns pequenos ajustes e inclui alguns dados específicos do taproot para assinar.

    Veja o exemplo de key path spend e o exemplo de script path spend (com assinatura) para exemplos de código reais.

    Mensagem de Assinatura Comum

    Campos da mensagem de assinatura comum do Taproot
    Campo Tamanho (bytes) Descrição
    Hash Type 1 Byte indicando a quantidade de dados da transação a ser assinada.
    Version 4 Campo version da transação.
    Locktime 4 Campo locktime da transação.
    Hash Prevouts 32 SHA-256 dos txid+vout de todas as entradas.
    Hash Amounts 32 SHA-256 de todos os campos amount das entradas.
    Hash ScriptPubKeys 32 SHA-256 de todos os scriptpubkeys das entradas (formato serializado).
    Nota: os dados de cada scriptpubkey são tamanho_do_scriptpubkey+scriptpubkey.
    Hash Sequences 32 SHA-256 de todos os campos sequence das entradas.
    Hash Outputs 32 SHA-256 de todas as saídas da transação.
    Nota: os dados de cada saída são amount+tamanho_do_scriptpubkey+scriptpubkey.
    Spend Type 1 Codifica extension flag e annex present.
    Input Outpoint 36 txid+vout da entrada sendo assinada.
    Input Amount 8 amount da entrada sendo assinada.
    Input ScriptPubKey variável scriptpubkey da entrada sendo assinada (serializado).
    Input Sequence 4 sequence da entrada sendo assinada.
    Input Index 4 Índice vin da entrada sendo assinada.
    Hash Annex 32 SHA-256 do anexo opcional (com prefixo compact size).
    Hash Single Output 32 SHA-256 da saída oposta à entrada sendo assinada (SIGHASH_SINGLE).
    Nota: os dados da saída são amount+tamanho_do_scriptpubkey+scriptpubkey.

    Quando cada campo é incluído:

    • Hash Prevouts, Hash Amounts, Hash ScriptPubKeys, Hash Sequences e Input Index — incluídos quando não for SIGHASH_ANYONECANPAY.
    • Hash Outputs — incluído quando não for SIGHASH_NONE nem SIGHASH_SINGLE.
    • Input Outpoint, Input Amount, Input ScriptPubKey e Input Sequence — incluídos quando for SIGHASH_ANYONECANPAY.
    • Hash Single Output — incluído quando for SIGHASH_SINGLE.

    Hash Type

    Indica quanto da transação você quer assinar.

    Tipos de hash SIGHASH
    Byte Tipo Descrição
    0x01SIGHASH_ALLAssina todas as entradas e saídas
    0x02SIGHASH_NONEAssina todas as entradas apenas
    0x03SIGHASH_SINGLEAssina todas as entradas e uma saída correspondente
    0x81SIGHASH_ANYONECANPAY | ALLAssina uma entrada e todas as saídas
    0x82SIGHASH_ANYONECANPAY | NONEAssina uma entrada apenas
    0x83SIGHASH_ANYONECANPAY | SINGLEAssina uma entrada e uma saída correspondente

    Hash Type Padrão

    O Taproot introduz um novo byte SIGHASH padrão:

    0x00 = SIGHASH_DEFAULT (equivalente a SIGHASH_ALL).

    Se nenhum byte de hash type for anexado à assinatura, SIGHASH_DEFAULT é usado.

    Spend Type

    O spend type codifica os valores extension flag e annex present em um único byte.

    spend type = (extension flag * 2) + annex present

    Extension Flag

    A extension flag indica se uma extensão foi adicionada ao final da mensagem de assinatura comum.

    • 0 = key path spend
    • 1 = script path spend (tapscript)

    A extensão só é usada se você estiver criando uma assinatura para um script path spend.

    Annex Present

    O valor annex present indica se o elemento opcional anexo foi incluído ao final do campo de testemunha.

    • 0 = não presente
    • 1 = presente

    Todos os scripts-folha atualmente usam tapscript, então a extension flag para todos os script path spends deve ser 1.

    O anexo não é usado atualmente, então annex present deve ser 0.

    Extensão da Mensagem de Assinatura Comum

    Diagrama mostrando como construir a extensão da mensagem de assinatura comum a partir da folha escolhida na árvore de scripts
    Como construir a extensão da mensagem de assinatura comum a partir da folha escolhida na árvore de scripts.

    Script path spends requerem uma extensão com informações extras sobre o script-folha escolhido:

    Campos da extensão da mensagem de assinatura
    Campo Tamanho (bytes) Descrição
    Leaf Hash 32 Hash da folha escolhida.
    Public Key Version 1 default = 0x00
    Codeseparator Position 4 none = 0xffffffff

    Para a maioria dos scripts-folha: public key version = 0x00 e codeseparator position = 0xffffffff.

    Hash de Assinatura

    É a "mensagem" que assinamos de fato. Um hash com tag da mensagem de assinatura comum com um prefixo sighash epoch e a extensão opcional.

    tag = "TapSighash"

    dados = epoch + mensagem + extensão

    Sighash Epoch

    0x00 = padrão

    O byte de sighash epoch é prefixado à mensagem de assinatura comum ao criar o hash de assinatura.

    É um byte extra que permite upgrades do algoritmo de assinatura no futuro. Usar este prefixo significa que a tag "TapSighash" pode ser reutilizada sem precisar usar tags diferentes para indicar mudanças de algoritmo no futuro.

    Apenas use 0x00 e não se preocupe com isso por agora.

    Assinatura

    A assinatura para gastos P2TR é criada com o esquema Schnorr:

    assinatura = schnorr_sign(chave privada, mensagem, aux rand)

    A assinatura final no testemunha tem o byte de hash type opcional anexado ao final. Se ausente, SIGHASH_DEFAULT é usado.

    Tapscript

    Diagrama mostrando a localização de um script tapscript no script-folha e no testemunha
    Localização de um script tapscript no script-folha e no testemunha.

    Tapscript é o mecanismo de travamento usado nos scripts-folha.

    É basicamente o mesmo que o Script.

    A única diferença é que alguns opcodes foram modificados para usar validação de assinatura Schnorr em vez de ECDSA, e um punhado de outros opcodes foram modificados ou desabilitados. Mas, na maior parte, funciona igual ao Script tradicional.

    A versão do tapscript é indicada pela versão da folha, o que permite novas variações de tapscript no futuro.

    Opcodes

    O Tapscript muda a operação dos seguintes opcodes:

    Mudanças de opcodes no Tapscript
    Byte Opcode Descrição
    0xac OP_CHECKSIG Agora usa verificação de assinatura Schnorr.
    0xad OP_CHECKSIGVERIFY Agora usa verificação de assinatura Schnorr.
    0xae OP_CHECKMULTISIG Desabilitado.
    0xaf OP_CHECKMULTISIGVERIFY Desabilitado.
    0xba OP_CHECKSIGADD Novo. Substitui OP_CHECKMULTISIG e OP_CHECKMULTISIGVERIFY. Permite scripts multisig mais eficientes com verificação em lote.
    OP_NOPX Renomeados para OP_SUCCESSX.

    São reservados para upgrades que exijam nova funcionalidade de opcode. Estes opcodes estavam previamente desabilitados, mas no tapscript (versão 192) a presença deles marca o script como válido (não importa onde apareçam).

    OP_NOPX refere-se aos seguintes opcodes: 0x50, 0x62, 0x7e-0x81, 0x83-0x86, 0x89-0x8a, 0x8d-0x8e, 0x95-0x99, 0xbb-0xfe.

    Limites

    Diagrama mostrando os limites de tamanho dos scripts tapscript
    Limites de tamanho dos scripts tapscript.

    O Tapscript tem limites maiores comparado ao Script:

    Orçamento de Sigops

    O Tapscript usa um "orçamento" para limitar operações de assinatura:

    Orçamento              = 50 + tamanho do testemunha
    Operação de Assinatura = -50

    Isso significa que cada tapscript pode incluir pelo menos 1 operação de assinatura, e mais dependendo do tamanho do script e da complexidade da árvore.

    Limite de Sigops Legado

    Este orçamento de sigops significa que o limite de sigops para tapscript é agora por script, em oposição ao limite por bloco. Isso facilita para os mineradores selecionarem transações para inclusão em seus blocos.

    O limite de sigops legado atua como uma restrição secundária além do limite de tamanho do bloco, então os mineradores precisam ser extra cautelosos sobre quais transações incluem em seu bloco.

    Exemplos

    Exemplos reais mostrando a construção e o gasto de saídas P2TR. Eles mostram dados brutos e código para as formas mais comuns de criar e gastar scripts de travamento P2TR.

    Estes exemplos são básicos, mas são ideais se você está programando sua própria implementação e quer verificar se os resultados que está obtendo ao longo do caminho estão corretos.

    Os exemplos de código Ruby requerem o arquivo examples-taproot-common.rb (veja o código comum abaixo).

    Código Comum (Ruby)

    Este é um arquivo de funções auxiliares comuns usadas em todos os exemplos de código abaixo. Você pode salvá-lo como examples-taproot-common.rb no mesmo diretório dos exemplos de código abaixo, ou copiar e colar o código no topo de cada um.

    # -------------------------
    # Parâmetros da Curva Elíptica
    # -------------------------
    # secp256k1 — a mesma curva usada no ECDSA
    
    # y² = x³ + ax + b
    $a = 0
    $b = 7
    
    # corpo primo
    $p = 115792089237316195423570985008687907853269984665640564039457584007908834671663
    
    # número de pontos na curva ("ordem")
    $n = 115792089237316195423570985008687907852837564279074904382605163141518161494337
    
    # ponto gerador
    $G = {
      x: 55066263022277343669578718895168534326250603453777594175500187360389116729240,
      y: 32670510020758816978083085130507043184471273380659243275938904335757337482424,
    }
    
    # --------------------------
    # Matemática de Curva Elíptica
    # --------------------------
    
    def inverse(a, m = $p)
      m_orig = m
      a = a % m if a < 0
      y_prev = 0
      y = 1
      while a > 1
        q = m / a
        y_before = y
        y = y_prev - q * y
        y_prev = y_before
        a_before = a
        a = m % a
        m = a_before
      end
      return y % m_orig
    end
    
    def double(point)
      if (((2 * point[:y]) % $p).gcd($p) != 1)
        raise "Ponto no infinito."
      end
      slope = ((3 * point[:x] ** 2 + $a) * inverse((2 * point[:y]), $p)) % $p
      x = (slope ** 2 - (2 * point[:x])) % $p
      y = (slope * (point[:x] - x) - point[:y]) % $p
      return { x: x, y: y }
    end
    
    def add(point1, point2)
      if point1 == point2
        return double(point1)
      end
      if ((point1[:x] - point2[:x]).gcd($p) != 1)
        raise "Ponto no infinito."
      end
      slope = ((point1[:y] - point2[:y]) * inverse(point1[:x] - point2[:x], $p)) % $p
      x = (slope ** 2 - point1[:x] - point2[:x]) % $p
      y = ((slope * (point1[:x] - x)) - point1[:y]) % $p
      return { x: x, y: y }
    end
    
    def multiply(k, point = $G)
      current = point
      binary = k.to_s(2)
      binary.split("").drop(1).each do |char|
        current = double(current)
        current = add(current, point) if char == "1"
      end
      current
    end
    
    # ----------------
    # BIP340 (Schnorr)
    # ----------------
    
    def int(bytes)
      return bytes.to_i(16)
    end
    
    def bytes(int)
      return int.to_s(16).rjust(64, "0")
    end
    
    def tagged_hash(tag, message)
      tag_hash = Digest::SHA256.hexdigest(tag)
      preimage = [tag_hash + tag_hash + message].pack("H*")
      result = Digest::SHA256.hexdigest(preimage)
      return result
    end
    
    def lift_x(public_key)
      x = int(public_key)
      y_sq = (x**3 + 7) % $p
      y = y_sq.pow(($p+1)/4, $p)
      if x >= $p
        raise "x inválido"
      end
      if (y**2) % $p != y_sq
        raise "chave inválida"
      end
      if y % 2 != 0
        y = $p - y
      end
      return {x: x, y: y}
    end
    
    # ----------------
    # BIP341 (Taproot)
    # ----------------
    
    def calculate_control_byte(leaf_version, tweaked_pubkey_point)
      if (tweaked_pubkey_point[:y] % 2 == 0)
        parity_bit = 0
      else
        parity_bit = 1
      end
      control_byte = field(dechex(leaf_version + parity_bit), 1)
      return control_byte
    end
    
    # -----------------
    # Funções Gerais
    # -----------------
    
    def dechex(dec)
      return dec.to_i.to_s(16)
    end
    
    def field(field, size=4)
      return field.to_s.rjust(size*2, '0')
    end
    
    def reversebytes(hex)
      return hex.to_s.scan(/../).reverse.join
    end
    
    def compact_size(i)
      if (i <= 252)
        result = field(dechex(i), 1)
      elsif (i > 252 && i <= 65535)
        result = 'fd' + field(dechex(i), 2)
      elsif (i > 65535  && i <= 4294967295)
        result = 'fe' + field(dechex(i), 4)
      elsif (i > 4294967295 && i <= 18446744073709551615)
        result = 'ff' + field(dechex(i), 8)
      end
      return result
    end
    
    def serialize_script(script)
      length = script.length / 2
      return compact_size(length) + script
    end

    1. Key Path Spend

    Este é um exemplo de key path spend.

    Este é o método "padrão" para gastar uma saída P2TR.

    Construir

    chave pública          = 924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329
    raiz de Merkle         =
    tweak                  = 8dc8b9030225e044083511759b58328b46dffcc78b920b4b97169f9d7b43d3b5
    chave pública ajustada = 0f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667
    scriptpubkey           = 51200f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667

    TXID: a7115c7267dbb4aab62b37818d431b784fe731f4d2f9fa0939a9980d581690ec (Saída 0)

    Este exemplo não usa uma árvore de scripts, e é por isso que a raiz de Merkle está vazia.

    Gastar

    chave privada          = 55d7c5a9ce3d2b15a62434d01205f3e59077d51316f5c20628b3a4b8b2a76f4c
    chave pública          = 924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329
    raiz de Merkle         =
    tweak                  = 8dc8b9030225e044083511759b58328b46dffcc78b920b4b97169f9d7b43d3b5
    chave privada ajustada = 37f0f35933e8b52e6210dca589523ea5b66827b4749c49456e62fae4c89c6469
    sighash                = a7b390196945d71549a2454f0185ece1b47c56873cf41789d78926852c355132
    aux rand               = 0000000000000000000000000000000000000000000000000000000000000000
    assinatura             = b693a0797b24bae12ed0516a2f5ba765618dca89b75e498ba5b745b71644362298a45ca39230d10a02ee6290a91cebf9839600f7e35158a447ea182ea0e022ae
    testemunha             = b693a0797b24bae12ed0516a2f5ba765618dca89b75e498ba5b745b71644362298a45ca39230d10a02ee6290a91cebf9839600f7e35158a447ea182ea0e022ae01

    TXID: 091d2aaadc409298fd8353a4cd94c319481a0b4623fb00872fe240448e93fcbe (Entrada 0)

    Estes dados de exemplo não mostram a construção do sighash a partir dos dados da transação original não assinada. Veja o código abaixo para os passos e dados completos.

    Código (Ruby)

    Este código requer o examples-taproot-common.rb.

    require_relative 'examples-taproot-common.rb'
    require "digest"
    
    # =========
    # Construct
    # =========
    
    internal_pubkey = '924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329'
    
    merkle_root = ""
    
    tweak = tagged_hash("TapTweak", internal_pubkey + merkle_root)
    #=> 8dc8b9030225e044083511759b58328b46dffcc78b920b4b97169f9d7b43d3b5
    
    t = int(tweak)
    internal_pubkey_point = lift_x(internal_pubkey)
    tweaked_pubkey_point = add(internal_pubkey_point, multiply(t, $G))
    tweaked_pubkey = bytes(tweaked_pubkey_point[:x])
    #=> 0f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667
    
    scriptpubkey = '51' + '20' + tweaked_pubkey
    #=> 51200f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667
    
    # =====
    # Spend
    # =====
    
    hash_type = 1
    hash_type_byte = field(dechex(hash_type), 1)
    ext_flag = 0
    annex_present = 0
    spend_type = field(dechex((ext_flag * 2) + annex_present), 1)
    
    version = '02000000'
    locktime = '00000000'
    
    prevouts = 'ec9016580d98a93909faf9d2f431e74f781b438d81372bb6aab4db67725c11a7' + '00000000'
    amounts = reversebytes(field(dechex('20000'), 8))
    sequences = 'ffffffff'
    scriptpubkeys = serialize_script('51200f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667')
    
    sha_prevouts = Digest::SHA256.hexdigest([prevouts].pack("H*"))
    sha_amounts = Digest::SHA256.hexdigest([amounts].pack("H*"))
    sha_sequences = Digest::SHA256.hexdigest([sequences].pack("H*"))
    sha_scriptpubkeys = Digest::SHA256.hexdigest([scriptpubkeys].pack("H*"))
    
    outputs = '1027000000000000' + serialize_script('00144e44ca792ce545acba99d41304460dd1f53be384')
    sha_outputs = Digest::SHA256.hexdigest([outputs].pack("H*"))
    
    input_index = '00000000'
    
    sha_annex = ''
    sha_single_output = ''
    
    sigmsg = hash_type_byte + version + locktime
    
    if (hash_type & 0x80 == 0)
      sigmsg += sha_prevouts + sha_amounts + sha_scriptpubkeys + sha_sequences
    end
    
    if (hash_type & 3 < 2)
      sigmsg += sha_outputs
    end
    
    sigmsg += spend_type
    
    if (hash_type & 0x80 == 0)
      sigmsg += input_index
    else
      sigmsg += input_outpoint + input_amount + input_scriptpubkey + input_sequence
    end
    
    sigmsg += sha_annex
    
    if (hash_type & 3 == 3)
      sigmsg += sha_single_output
    end
    
    epoch = '00'
    sighash = tagged_hash("TapSighash", epoch + sigmsg)
    
    # Tweak Private Key
    private_key = '55d7c5a9ce3d2b15a62434d01205f3e59077d51316f5c20628b3a4b8b2a76f4c'
    private_key_int = int(private_key)
    private_key_to_public_key = multiply(private_key_int)
    
    if private_key_to_public_key[:y] % 2 == 1
      private_key_int_negated = $n - private_key_int
    else
      private_key_int_negated = private_key_int
    end
    
    tweaked_privkey_int = (private_key_int_negated + int(tweak)) % $n
    tweaked_privkey = bytes(tweaked_privkey_int)
    #=> 37f0f35933e8b52e6210dca589523ea5b66827b4749c49456e62fae4c89c6469
    
    # Sign
    private_key = tweaked_privkey
    message = sighash
    aux_rand = '0000000000000000000000000000000000000000000000000000000000000000'
    
    d0 = int(private_key)
    unless (1..$n-1).include?(d0)
      raise "private key must be in the range 1..n-1"
    end
    public_key_point = multiply(d0)
    if public_key_point[:y] % 2 != 0
      d = $n - d0
    else
      d = d0
    end
    aux_rand_hash = tagged_hash("BIP0340/aux", aux_rand)
    t = d ^ int(aux_rand_hash)
    k0 = int(tagged_hash("BIP0340/nonce", bytes(t) + bytes(public_key_point[:x]) + message)) % $n
    if k0 == 0
      raise "nonce must not be zero"
    end
    random_point = multiply(k0)
    if random_point[:y] % 2 != 0
      k = $n - k0
    else
      k = k0
    end
    e = int(tagged_hash("BIP0340/challenge", bytes(random_point[:x]) + bytes(public_key_point[:x]) + message)) % $n
    r = random_point[:x]
    s = (k + e * d) % $n
    sig = bytes(r) + bytes(s)
    #=> b693a0797b24bae12ed0516a2f5ba765618dca89b75e498ba5b745b71644362298a45ca39230d10a02ee6290a91cebf9839600f7e35158a447ea182ea0e022ae
    
    witness = sig + hash_type_byte
    #=> b693a0797b24bae12ed0516a2f5ba765618dca89b75e498ba5b745b71644362298a45ca39230d10a02ee6290a91cebf9839600f7e35158a447ea182ea0e022ae01

    2. Script Path Spend (Simples)

    Este é um exemplo de script path spend básico.

    A árvore de scripts contém uma folha. O script-folha é um script básico OP_8 OP_EQUAL que não requer assinatura.

    É mais fácil compreender os script path spends da primeira vez usando um script-folha que não requer assinatura.

    Este script não é seguro. Estou usando este script básico apenas para mostrar a mecânica de um script path spend. Se você usar este tipo de script no mundo real, suas moedas provavelmente serão roubadas quando você transmitir a transação de gasto. Isso realmente aconteceu comigo quando gastei esta saída — alguém estava monitorando a rede e pegou minhas moedas antes que eu conseguisse concluir minha segunda transação.

    Construir

    chave pública          = 924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329
    versão da folha        = c0
    script-folha           = 5887
    hash da folha          = e4b47d76d2f78791323a035811c350bb7875568006cb60f0e171efb70c11bda4
    raiz de Merkle         = e4b47d76d2f78791323a035811c350bb7875568006cb60f0e171efb70c11bda4
    tweak                  = fe9007dc134942f8aabcc1af9e1c201c454d333501efccfbb8abf05bdfcb09a5
    chave pública ajustada = 1baeaaf9047cc42055a37a3ac981bdf7f5ab96fad0d2d07c54608e8a181b9477
    scriptpubkey           = 51201baeaaf9047cc42055a37a3ac981bdf7f5ab96fad0d2d07c54608e8a181b9477

    TXID: 8bc4f8facaaf7c4bdf6d77fac90aea208c2099a091d4b09658d002739daaad87 (Saída 1)

    Esta árvore de scripts contém apenas uma folha, então a raiz de Merkle resultante é igual àquele hash da folha.

    Gastar

    script inputs       = 08
    script-folha        = 5887
    byte de controle    = c1
    chave pública       = 924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329
    caminho de Merkle   =
    bloco de controle   = c1924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329
    testemunha          = 03010802588721c1924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329

    TXID: 5ff05f74d385bd39e344329330461f74b390c1b5ead87c4f51b40c555b75719d (Entrada 1)

    A entrada do script é 08, que satisfaz o script-folha original de OP_8 OP_EQUAL.

    Nenhum caminho de Merkle é necessário para gastar usando este script-folha, porque a árvore de scripts contém apenas uma folha e a raiz de Merkle pode ser reconstruída apenas a partir do único script-folha.

    O testemunha resultante é a codificação de testemunha dos seguintes itens: entradas do script + script-folha + bloco de controle.

    O byte de controle é c1 em vez de c0, pois a paridade da chave pública ajustada é ímpar.

    Código (Ruby)

    Este código requer o examples-taproot-common.rb.

    require_relative 'examples-taproot-common.rb'
    require "digest"
    
    internal_pubkey = '924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329'
    
    leaf_version = 192
    script = '5887'
    leaf_hash = tagged_hash("TapLeaf", field(dechex(leaf_version), 1) + serialize_script(script))
    #=> e4b47d76d2f78791323a035811c350bb7875568006cb60f0e171efb70c11bda4
    
    merkle_root = leaf_hash
    
    tweak = tagged_hash("TapTweak", internal_pubkey + merkle_root)
    #=> fe9007dc134942f8aabcc1af9e1c201c454d333501efccfbb8abf05bdfcb09a5
    
    t = int(tweak)
    internal_pubkey_point = lift_x(internal_pubkey)
    tweaked_pubkey_point = add(internal_pubkey_point, multiply(t, $G))
    tweaked_pubkey = bytes(tweaked_pubkey_point[:x])
    #=> 1baeaaf9047cc42055a37a3ac981bdf7f5ab96fad0d2d07c54608e8a181b9477
    
    scriptpubkey = '51' + '20' + tweaked_pubkey
    #=> 51201baeaaf9047cc42055a37a3ac981bdf7f5ab96fad0d2d07c54608e8a181b9477
    
    # Spend
    merkle_path = ''
    control_byte = calculate_control_byte(192, tweaked_pubkey_point)
    control_block = control_byte + internal_pubkey
    #=> c1924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329
    
    script_inputs = '08'
    script        = '5887'
    control_block = control_block
    
    witness =
      compact_size(3) +
      compact_size(script_inputs.length / 2) + script_inputs +
      compact_size(script.length / 2) + script +
      compact_size(control_block.length / 2) + control_block
    #=> 03015802588721c1924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329

    3. Script Path Spend (Com Assinatura)

    Este é outro exemplo de script path spend, mas desta vez o script-folha requer uma assinatura para destravá-lo.

    Isso significa que você precisa usar o algoritmo de assinatura para script path spends, que é ligeiramente diferente do algoritmo usado em key path spends.

    Construir

    chave pública          = 924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329
    versão da folha        = c0
    script-folha           = 206d4ddc0e47d2e8f82cbe2fc2d0d749e7bd3338112cecdc76d8f831ae6620dbe0ac
    hash da folha          = 858dfe26a3dd48a2c1fcee1d631f0aadf6a61135fc51f75758e945bca534ef16
    raiz de Merkle         = 858dfe26a3dd48a2c1fcee1d631f0aadf6a61135fc51f75758e945bca534ef16
    tweak                  = 479785dd89a6441dbe00c7661865a0cc68672e8021f4547ac7f89ac26ac049f2
    chave pública ajustada = f3778defe5173a9bf7169575116224f961c03c725c0e98b8da8f15df29194b80
    scriptpubkey           = 5120f3778defe5173a9bf7169575116224f961c03c725c0e98b8da8f15df29194b80

    TXID: d1c40446c65456a9b11a9dddede31ee34b8d3df83788d98f690225d2958bfe3c (Saída 0)

    Gastar

    chave privada do script = 9b8de5d7f20a8ebb026a82babac3aa47a008debbfde5348962b2c46520bd5189
    sighash             = 752453d473e511a0da2097d664d69fe5eb89d8d9d00eab924b42fc0801a980c9
    aux rand            = 0000000000000000000000000000000000000000000000000000000000000000
    assinatura          = 01769105cbcbdcaaee5e58cd201ba3152477fda31410df8b91b4aee2c4864c7700615efb425e002f146a39ca0a4f2924566762d9213bd33f825fad83977fba7f01
    script-folha        = 206d4ddc0e47d2e8f82cbe2fc2d0d749e7bd3338112cecdc76d8f831ae6620dbe0ac
    byte de controle    = c0
    chave pública       = 924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329
    caminho de Merkle   =
    bloco de controle   = c0924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329
    testemunha          = 034101769105cbcbdcaaee5e58cd201ba3152477fda31410df8b91b4aee2c4864c7700615efb425e002f146a39ca0a4f2924566762d9213bd33f825fad83977fba7f0122206d4ddc0e47d2e8f82cbe2fc2d0d749e7bd3338112cecdc76d8f831ae6620dbe0ac21c0924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329

    TXID: 797505b104b5fb840931c115ea35d445eb1f64c9279bf23aa5bb4c3d779da0c2 (Entrada 0)

    Para criar uma assinatura para destravar este script-folha, você precisa usar a chave privada da chave pública no script-folha (e não a da chave pública usada em um key path spend).

    O testemunha resultante é a codificação de testemunha dos seguintes itens: assinatura + script-folha + bloco de controle.

    Estes dados de exemplo não mostram a construção do sighash a partir dos dados da transação original não assinada. Veja o código abaixo para os passos e dados completos.

    Código (Ruby)

    Este código requer o examples-taproot-common.rb.

    require_relative 'examples-taproot-common.rb'
    require "digest"
    
    internal_pubkey = '924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329'
    
    leaf_version = 192
    script = '206d4ddc0e47d2e8f82cbe2fc2d0d749e7bd3338112cecdc76d8f831ae6620dbe0ac'
    leaf_hash = tagged_hash("TapLeaf", field(dechex(leaf_version), 1) + serialize_script(script))
    #=> 858dfe26a3dd48a2c1fcee1d631f0aadf6a61135fc51f75758e945bca534ef16
    
    merkle_root = leaf_hash
    
    tweak = tagged_hash("TapTweak", internal_pubkey + merkle_root)
    #=> 479785dd89a6441dbe00c7661865a0cc68672e8021f4547ac7f89ac26ac049f2
    
    t = int(tweak)
    internal_pubkey_point = lift_x(internal_pubkey)
    tweaked_pubkey_point = add(internal_pubkey_point, multiply(t, $G))
    tweaked_pubkey = bytes(tweaked_pubkey_point[:x])
    #=> f3778defe5173a9bf7169575116224f961c03c725c0e98b8da8f15df29194b80
    
    scriptpubkey = '51' + '20' + tweaked_pubkey
    #=> 5120f3778defe5173a9bf7169575116224f961c03c725c0e98b8da8f15df29194b80
    
    # Spend
    control_byte = calculate_control_byte(192, tweaked_pubkey_point)
    control_block = control_byte + internal_pubkey
    #=> c0924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329
    
    hash_type = 1
    hash_type_byte = field(dechex(hash_type), 1)
    ext_flag = 1
    annex_present = 0
    spend_type = field(dechex((ext_flag * 2) + annex_present), 1)
    
    version = '02000000'
    locktime = '00000000'
    
    prevouts = '3cfe8b95d22502698fd98837f83d8d4be31ee3eddd9d1ab1a95654c64604c4d1' + '00000000'
    amounts = reversebytes(field(dechex('20000'), 8))
    sequences = 'ffffffff'
    scriptpubkeys = serialize_script('5120f3778defe5173a9bf7169575116224f961c03c725c0e98b8da8f15df29194b80')
    
    sha_prevouts = Digest::SHA256.hexdigest([prevouts].pack("H*"))
    sha_amounts = Digest::SHA256.hexdigest([amounts].pack("H*"))
    sha_sequences = Digest::SHA256.hexdigest([sequences].pack("H*"))
    sha_scriptpubkeys = Digest::SHA256.hexdigest([scriptpubkeys].pack("H*"))
    
    outputs = '983a000000000000' + serialize_script('00140de745dc58d8e62e6f47bde30cd5804a82016f9e')
    sha_outputs = Digest::SHA256.hexdigest([outputs].pack("H*"))
    
    input_index = '00000000'
    sha_annex = ''
    sha_single_output = ''
    
    sigmsg = hash_type_byte + version + locktime
    if (hash_type & 0x80 == 0)
      sigmsg += sha_prevouts + sha_amounts + sha_scriptpubkeys + sha_sequences
    end
    if (hash_type & 3 < 2)
      sigmsg += sha_outputs
    end
    sigmsg += spend_type
    if (hash_type & 0x80 == 0)
      sigmsg += input_index
    else
      sigmsg += input_outpoint + input_amount + input_scriptpubkey + input_sequence
    end
    if (annex_present == 1)
      annex = ''
      sha_annex = Digest::SHA256.hexdigest([annex].pack("H*"))
      sigmsg += sha_annex
    end
    if (hash_type & 3 == 3)
      sigmsg += sha_single_output
    end
    
    if (ext_flag == 1)
      extension = leaf_hash + '00' + 'ffffffff'
      sigmsg += extension
    end
    
    sighash = tagged_hash("TapSighash", '00' + sigmsg)
    #=> 752453d473e511a0da2097d664d69fe5eb89d8d9d00eab924b42fc0801a980c9
    
    private_key = '9b8de5d7f20a8ebb026a82babac3aa47a008debbfde5348962b2c46520bd5189'
    message = sighash
    aux_rand = '0000000000000000000000000000000000000000000000000000000000000000'
    
    d0 = int(private_key)
    unless (1..$n-1).include?(d0)
      raise "private key must be in the range 1..n-1"
    end
    public_key_point = multiply(d0)
    if public_key_point[:y] % 2 != 0
      d = $n - d0
    else
      d = d0
    end
    aux_rand_hash = tagged_hash("BIP0340/aux", aux_rand)
    t = d ^ int(aux_rand_hash)
    k0 = int(tagged_hash("BIP0340/nonce", bytes(t) + bytes(public_key_point[:x]) + message)) % $n
    if k0 == 0
      raise "nonce must not be zero"
    end
    random_point = multiply(k0)
    if random_point[:y] % 2 != 0
      k = $n - k0
    else
      k = k0
    end
    e = int(tagged_hash("BIP0340/challenge", bytes(random_point[:x]) + bytes(public_key_point[:x]) + message)) % $n
    r = random_point[:x]
    s = (k + e * d) % $n
    sig = bytes(r) + bytes(s)
    #=> 01769105cbcbdcaaee5e58cd201ba3152477fda31410df8b91b4aee2c4864c7700615efb425e002f146a39ca0a4f2924566762d9213bd33f825fad83977fba7f
    
    signature = sig + hash_type_byte
    script_inputs = signature
    script        = '206d4ddc0e47d2e8f82cbe2fc2d0d749e7bd3338112cecdc76d8f831ae6620dbe0ac'
    control_block = control_block
    
    witness =
      compact_size(3) +
      compact_size(script_inputs.length / 2) + script_inputs +
      compact_size(script.length / 2) + script +
      compact_size(control_block.length / 2) + control_block
    #=> 034101769105cbcbdcaaee5e58cd201ba3152477fda31410df8b91b4aee2c4864c7700615efb425e002f146a39ca0a4f2924566762d9213bd33f825fad83977fba7f0122206d4ddc0e47d2e8f82cbe2fc2d0d749e7bd3338112cecdc76d8f831ae6620dbe0ac21c0924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329

    4. Script Path Spend (Árvore Complexa)

    Este é outro exemplo de script path spend, mas desta vez usando uma árvore de scripts complexa.

    Este exemplo mostra como construir uma árvore de scripts complexa, bem como o caminho de Merkle resultante necessário ao gastar para provar que seu script-folha escolhido fazia parte da árvore original.

    Para os fins deste exemplo, os scripts-folha são extremamente básicos e não requerem assinatura (ex.: OP_N OP_EQUAL).

    Estes scripts de folha não são seguros. Se você usar este tipo de script no mundo real, suas moedas provavelmente serão roubadas quando você transmitir a transação de gasto (o que realmente aconteceu comigo quando gastei esta saída).

    Construir

    chave pública       = 924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329
    
    ┌────────┐ ┌────────┐
    │ folha 1│ │ folha 2│
    └───┬────┘ └───┬────┘
        └────┬─────┘
         ┌───┴────┐ ┌────────┐
         │ ramo 1 │ │ folha 3│
         └───┬────┘ └─────┬──┘
             └─────┬──────┘
               ┌───┴────┐ ┌────────┐
               │ ramo 2 │ │ folha 4│
               └───┬────┘ └────┬───┘
                   └────┬──────┘
                    ┌───┴────┐ ┌────────┐
                    │ ramo 3 │ │ folha 5│
                    └───┬────┘ └────┬───┘
                        └─────┬─────┘
                          ┌───┴────┐
                          │ ramo 4 │
                          └────────┘
    
    versão da folha 1     = c0
    tamanho(script folha 1) = 02
    script da folha 1     = 5187
    hash da folha 1       = 6b13becdaf0eee497e2f304adcfa1c0c9e84561c9989b7f2b5fc39f5f90a60f6
    versão da folha 2     = c0
    tamanho(script folha 2) = 02
    script da folha 2     = 5287
    hash da folha 2       = ed5af8352e2a54cce8d3ea326beb7907efa850bdfe3711cef9060c7bb5bcf59e
    versão da folha 3     = c0
    tamanho(script folha 3) = 02
    script da folha 3     = 5387
    hash da folha 3       = 160bd30406f8d5333be044e6d2d14624470495da8a3f91242ce338599b233931
    versão da folha 4     = c0
    tamanho(script folha 4) = 02
    script da folha 4     = 5487
    hash da folha 4       = bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d
    versão da folha 5     = c0
    tamanho(script folha 5) = 02
    script da folha 5     = 5587
    hash da folha 5       = 54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f
    
    ramo 1 (folha 1 + folha 2)   = 1324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9
    ramo 2 (ramo 1 + folha 3)    = beec0122bddd26f642140bcd922e0264ce1e2be5808a41ae58d82e829bc913d7
    ramo 3 (ramo 2 + folha 4)    = a4e0d9cc12ce2f32069e98247581d5eb9ca0a4cf175771a8df2c53a93dcb0ebd
    ramo 4 (folha 5 + ramo 3)    = b5b72eea07b3e338962944a752a98772bbe1f1b6550e6fb6ab8c6e6adb152e7c
    
    raiz de Merkle      = b5b72eea07b3e338962944a752a98772bbe1f1b6550e6fb6ab8c6e6adb152e7c
    tweak               = 28dcaf275e25b339c2b8362dd0db3347fc7336602b2b52d95ffae0149038776c
    chave pública ajustada = 979cff99636da1b0e49f8711514c642f640d1f64340c3784942296368fadd0a5
    scriptpubkey        = 5120979cff99636da1b0e49f8711514c642f640d1f64340c3784942296368fadd0a5

    TXID: ec7b0fdfeb2c115b5a4b172a3a1cf406acc2425229c540d40ec752d893aac0d7 (Saída 0)

    Este exemplo usa uma árvore desbalanceada. Você usaria este tipo de estrutura quando a folha 5 é mais provável de ser executada que as outras folhas. Neste exemplo eu vou, na verdade, gastar esta saída usando a folha 3.

    Não se esqueça de que o hash de folha/ramo menor vem primeiro ao combinar hashes de folha/ramo anteriores para construir o próximo hash de ramo. Isso se aplica ao hash do ramo 4 neste exemplo (isto é, o hash da folha 5 vem antes do hash do ramo 3).

    A raiz de Merkle é o hash final da árvore, que é o ramo 4.

    Gastar (folha 3)

    script inputs       = 03
    script              = 5387
    byte de controle    = c0
    chave pública       = 924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329
    
    ┌────────┐ ┌────────┐
    │ folha 1│ │ folha 2│
    └───┬────┘ └───┬────┘
        └────┬─────┘
         X───┴────┐ ┌────────┐
         │ ramo 1 │ │ folha 3│ <- gastando
         └───┬────┘ └─────┬──┘
             └─────┬──────┘
               ┌───┴────┐ X────────┐
               │ ramo 2 │ │ folha 4│
               └───┬────┘ └────┬───┘
                   └────┬──────┘
                    ┌───┴────┐ X────────┐
                    │ ramo 3 │ │ folha 5│
                    └───┬────┘ └────┬───┘
                        └─────┬─────┘
                          ┌───┴────┐
                          │ ramo 4 │
                          └────────┘   Nota: X = hashes necessários para construir o caminho de Merkle
    
    caminho de Merkle   = 1324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f
    bloco de controle   = c0924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a33291324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f
    testemunha          = 03010302538781c0924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a33291324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f

    TXID: 992af7eb67f37a4dfaa64ea6f03a70c35b6063ba5ee3fe41734c3460b4006463 (Entrada 0)

    Este P2TR está sendo destravado usando a folha 3.

    A entrada do script é 03, que satisfaz o script-folha original de OP_3 OP_EQUAL.

    O caminho de Merkle é hash do ramo 1 + hash da folha 4 + hash da folha 5. Estes são os hashes necessários (em ordem) para reconstruir a raiz de Merkle durante a validação (o que prova que a folha 3 fazia parte da árvore de scripts original). Como você pode ver, nenhum dos scripts brutos é de fato revelado dentro do bloco de controle.

    O testemunha resultante é a codificação de testemunha dos seguintes itens: entradas do script + script-folha + bloco de controle.

    Código (Ruby)

    Este código requer o examples-taproot-common.rb.

    require_relative 'examples-taproot-common.rb'
    require "digest"
    
    internal_pubkey = '924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a3329'
    
    leaf_1_version = 192
    leaf_1_script = '5187'
    leaf_1_hash = tagged_hash("TapLeaf", field(dechex(leaf_1_version), 1) + serialize_script(leaf_1_script))
    #=> 6b13becdaf0eee497e2f304adcfa1c0c9e84561c9989b7f2b5fc39f5f90a60f6
    
    leaf_2_version = 192
    leaf_2_script = '5287'
    leaf_2_hash = tagged_hash("TapLeaf", field(dechex(leaf_2_version), 1) + serialize_script(leaf_2_script))
    #=> ed5af8352e2a54cce8d3ea326beb7907efa850bdfe3711cef9060c7bb5bcf59e
    
    leaf_3_version = 192
    leaf_3_script = '5387'
    leaf_3_hash = tagged_hash("TapLeaf", field(dechex(leaf_3_version), 1) + serialize_script(leaf_3_script))
    #=> 160bd30406f8d5333be044e6d2d14624470495da8a3f91242ce338599b233931
    
    leaf_4_version = 192
    leaf_4_script = '5487'
    leaf_4_hash = tagged_hash("TapLeaf", field(dechex(leaf_4_version), 1) + serialize_script(leaf_4_script))
    #=> bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d
    
    leaf_5_version = 192
    leaf_5_script = '5587'
    leaf_5_hash = tagged_hash("TapLeaf", field(dechex(leaf_5_version), 1) + serialize_script(leaf_5_script))
    #=> 54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f
    
    branch_1_hash = tagged_hash("TapBranch", leaf_1_hash + leaf_2_hash)
    #=> 1324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9
    
    branch_2_hash = tagged_hash("TapBranch", branch_1_hash + leaf_3_hash)
    #=> beec0122bddd26f642140bcd922e0264ce1e2be5808a41ae58d82e829bc913d7
    
    branch_3_hash = tagged_hash("TapBranch", branch_2_hash + leaf_4_hash)
    #=> a4e0d9cc12ce2f32069e98247581d5eb9ca0a4cf175771a8df2c53a93dcb0ebd
    
    branch_4_hash = tagged_hash("TapBranch", leaf_5_hash + branch_3_hash)
    #=> b5b72eea07b3e338962944a752a98772bbe1f1b6550e6fb6ab8c6e6adb152e7c
    
    merkle_root = branch_4_hash
    
    tweak = tagged_hash("TapTweak", internal_pubkey + merkle_root)
    #=> 28dcaf275e25b339c2b8362dd0db3347fc7336602b2b52d95ffae0149038776c
    
    t = int(tweak)
    internal_pubkey_point = lift_x(internal_pubkey)
    tweaked_pubkey_point = add(internal_pubkey_point, multiply(t, $G))
    tweaked_pubkey = bytes(tweaked_pubkey_point[:x])
    #=> 979cff99636da1b0e49f8711514c642f640d1f64340c3784942296368fadd0a5
    
    scriptpubkey = '51' + '20' + tweaked_pubkey
    #=> 5120979cff99636da1b0e49f8711514c642f640d1f64340c3784942296368fadd0a5
    
    # Spend
    control_byte = calculate_control_byte(192, tweaked_pubkey_point)
    merkle_path = branch_1_hash + leaf_4_hash + leaf_5_hash
    control_block = control_byte + internal_pubkey + merkle_path
    
    script_inputs = '03'
    script = '5387'
    
    witness =
      compact_size(3) +
      compact_size(script_inputs.length / 2) + script_inputs +
      compact_size(script.length / 2) + script +
      compact_size(control_block.length / 2) + control_block
    #=> 03010302538781c0924c163b385af7093440184af6fd6244936d1288cbb41cc3812286d3f83a33291324300a84045033ec539f60c70d582c48b9acf04150da091694d83171b44ec9bf2c4bf1ca72f7b8538e9df9bdfd3ba4c305ad11587f12bbfafa00d58ad6051d54962df196af2827a86f4bde3cf7d7c1a9dcb6e17f660badefbc892309bb145f

    História

    A ideia do Taproot foi proposta por Gregory Maxwell em 2018.

    Os BIPs detalhando as mudanças técnicas específicas foram organizados por Pieter Wuille e lançados em 2020:

    O Taproot foi ativado no Bitcoin em através de uma soft fork (a primeira desde o SegWit em 2017).

    Implantação

    A implantaçāo usou sinalização por version bits.

    Para a atualização ser aceita, 90% dos mineradores precisavam sinalizar a favor durante um período de ajuste de alvo de 2.016 blocos dentro da seguinte janela:

    Janela de ativação do Taproot
    Início Fim

    Se 90% dos mineradores sinalizassem prontidão durante qualquer período de ajuste, a atualização seria ativada na altura mínima de ativação de 709.632.

    Resultados da Sinalização

    Resultados da sinalização do Taproot por período de ajuste de alvo
    Início Período de Alvo Blocos Sinalizados Porcentagem Nota
    679.392 a 681.407 0/2016 0,00%
    681.408 a 683.423 823/2016 40,82% Primeiro período na janela.
    683.424 a 685.439 1690/2016 83,83%
    685.440 a 687.455 1983/2016 98,36% Período bem-sucedido
    687.456 a 689.471 2011/2016 99,75%
    689.472 a 691.487 2010/2016 99,70%
    691.488 a 693.503 2013/2016 99,85% Último período na janela
    693.504 a 695.519 1990/2016 98,71%
    695.520 a 697.535 1957/2016 97,07%

    O limite de 90% foi excedido pela primeira vez em , com 98,36% (1983/2016) dos blocos sinalizando a favor.

    O Taproot foi então ativado na altura do bloco 709.632 em .

    Etimologia

    Uma raiz pivotante (taproot) é uma raiz grande, central e dominante da qual outras raízes brotam lateralmente.
    Wikipédia

    O nome "Taproot" foi cunhado por Gregory Maxwell:

    O nome originou-se de uma visualização de uma árvore com um tronco central grosso como a raiz pivotante de um dente-de-leão — a técnica é mais útil porque pressupõe que existe um caminho de alta probabilidade e o resto são ramificações difusas, e pensei que era um bom nome pelo fato engraçado de que verifica gastos por caminho de script ao "tocar" (tap into) o compromisso oculto na raiz.
    Gregory Maxwell, Bitcoin Optech Github (Pull Request 667)

    A ideia é que a condição de gasto primária (gasto por caminho de chave) é a raiz pivotante, e todas as outras condições de gasto possíveis (gastos por caminho de script) são as raízes menores ramificando-se da raiz principal.

    O nome também vem do fato de que todas as condições de gasto adicionais convergem em uma única raiz de Merkle.

    Ilustração da raiz de um dente-de-leão mostrando como a raiz pivotante principal corresponde a um gasto por caminho de chave e as raízes menores correspondem a gastos por caminho de script
    Ilustração do dente-de-leão por Janine Wiget.

    Resumo

    O Taproot essencialmente introduziu o mecanismo de travamento "definitivo" para bitcoins.

    Este novo script de travamento P2TR fornece toda a funcionalidade de todos os scripts de travamento anteriores em um único script. Em outras palavras: se você pudesse ter apenas um script de travamento no Bitcoin, seria o P2TR.

    Do ponto de vista técnico, o P2TR simplesmente combina um bloqueio padrão de chave pública com a opção de incluir múltiplas condições de gasto personalizadas. É como um P2WPKH e um P2WSH em um só, com assinaturas Schnorr e tapscript para completar.

    É muito útil, e bem engenhoso.

    Entender como o P2TR funciona é muito mais fácil se você já conhece árvores de Merkle e criptografia de curva elíptica. Mas mesmo que não conheça, implementá-lo em código existente não é tão difícil; a parte mais complicada é não esquecer todos os irritantes bytes adicionais que precisam ser incluídos ao longo do caminho (ex.: o sighash epoch).

    Mas depois que você entender, o P2TR é um script de travamento extremamente versátil para se ter em seu kit de ferramentas.

    Recursos