Taproot
A grande atualização de 2021
BIP 341: Taproot: Regras de gasto SegWit versão 1
BIP 342: Validação de Scripts Taproot
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:
- Assinaturas Schnorr — O novo script de travamento P2TR usa o esquema de assinatura Schnorr, mais eficiente que o ECDSA.
- 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?
Um script de travamento P2TR pode ser destravado de uma de duas formas:
- 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.
- 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:
- 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.
- 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.
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?
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 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:
- 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) - Script — Seu script de travamento personalizado (tapscript).
Exemplo
versão da folha = c0
script da folha = 5187 - 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)?
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
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.
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:
- Tag = "TapLeaf"
- Dados =
versão da folha+tamanho(script)+script
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
Script
Compact Size
Tagged Hash
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:
- Tag = "TapBranch"
- Dados =
hash de folha/ramo menor+hash de folha/ramo maior
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:
- Tag = "TapTweak"
- Dados =
chave pública+raiz de Merkle
Exemplo
chave pública = a2fc329a085d8cfc4fa28795993d7b666cee024e94c40115141b8e9be4a29fa4
raiz de Merkle = b5b72eea07b3e338962944a752a98772bbe1f1b6550e6fb6ab8c6e6adb152e7c
tweak = tagged_hash("TapTweak", chave pública + raiz de Merkle)
tweak = bf0094eae70ba67e2f9fc3c4b81f078c90931855a8d24c959619174c92060cde
Tagged Hash (TapTweak)
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:
- Multiplique o ponto gerador pelo tweak para criar um "ponto de tweak".
- 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)
EC Multiply
EC Add
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)
Script
- Um ScriptPubKey P2TR tem sempre 34 bytes.
- P2TR usa
OP_1, enquanto P2WPKH e P2WSH usamOP_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 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:
- Key Path Spend — Gasto via chave pública.
- 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.
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
Schnorr Sign
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:
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)
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.
Um script path spend requer 3 coisas:
- Um script-folha da árvore de scripts.
- As entradas necessárias para destravar aquele script-folha.
- 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:
- Você precisa usar
extension flag = 1. - 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:
par = 0ímpar = 1
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:
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:
- O tamanho do script-folha.
- Quantas entradas de script são necessárias (isto é, a complexidade do script-folha).
- Quão acima da árvore o script-folha estava (isto é, quantos hashes de folha/ramo precisam ser fornecidos no bloco de controle).
Algoritmo de Assinatura
Como criar uma assinatura para um P2TR?
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
| 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_NONEnemSIGHASH_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.
| Byte | Tipo | Descrição |
|---|---|---|
0x01 | SIGHASH_ALL | Assina todas as entradas e saídas |
0x02 | SIGHASH_NONE | Assina todas as entradas apenas |
0x03 | SIGHASH_SINGLE | Assina todas as entradas e uma saída correspondente |
0x81 | SIGHASH_ANYONECANPAY | ALL | Assina uma entrada e todas as saídas |
0x82 | SIGHASH_ANYONECANPAY | NONE | Assina uma entrada apenas |
0x83 | SIGHASH_ANYONECANPAY | SINGLE | Assina 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 spend1= 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 presente1= 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
Script path spends requerem uma extensão com informações extras sobre o script-folha escolhido:
| 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)
- chave privada — Para key path spends, é a chave privada ajustada. Para script path spends, é a chave privada da chave pública no script-folha.
- mensagem — O hash de assinatura.
- aux rand — 32 bytes extras gerados aleatoriamente.
A assinatura final no testemunha tem o byte de hash type opcional anexado ao final. Se ausente, SIGHASH_DEFAULT é usado.
Tapscript
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:
| 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
O Tapscript tem limites maiores comparado ao Script:
- Tamanho total: Script: max 10.000 bytes — Tapscript: sem limite
- Opcodes: Script: max 201 opcodes não-push — Tapscript: sem limite
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:
- BIP 340: Assinaturas Schnorr para secp256k1
- BIP 341: Taproot: Regras de gasto SegWit versão 1
- BIP 342: Validação de Scripts Taproot
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.
- Version Bit =
2
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:
| 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
| 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.
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.
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.
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.