WEB3DEV

Cover image for Módulo de Planejamento do Rust-Miniscript: Transações Eficientes de Bitcoin
Adriano P. Araujo
Adriano P. Araujo

Posted on

Módulo de Planejamento do Rust-Miniscript: Transações Eficientes de Bitcoin

Trabalhei no projeto Rust-Miniscript durante o meu estágio no Summer of Bitcoin 2023. Este post no blog trata sobre o que é o Módulo de Planejamento, por que precisamos dele e como ele resolve problemas no mundo do Bitcoin. Vamos gradualmente abordar o trabalho que fiz ao longo do estágio para melhorar o Módulo de Planejamento.

Foto por Michael Förtsch noUnsplash

Antes de entrarmos nos detalhes do Módulo de Planejamento, gostaria de fornecer informações sobre todas as partes do projeto e minha compreensão sobre elas. O artigo está dividido em seções e você pode navegar nas seções com as quais mais se identificar. Ler todas as seções deve ajudar os novatos no desenvolvimento do Bitcoin a obterem insights mais claros. Vamos começar.

Scripts do Bitcoin

O Script do Bitcoin é uma linguagem de programação simples baseada em pilha (stack) usada no protocolo Bitcoin para definir as condições de gasto para transações de Bitcoin. É uma parte fundamental de como ablockchain do Bitcoin garante a segurança e o controle sobre a movimentação de fundos. O Script do Bitcoin permite que os usuários especifiquem as regras que devem ser atendidas para gastar os fundos bloqueados em uma saída específica (também conhecida como "UTXO" ou unspent transaction output).

Cada transação do Bitcoin inclui uma ou mais entradas e saídas. A entrada de uma transação referencia uma saída anterior (UTXO) e fornece os dados necessários para satisfazer as condições de gasto definidas pelo script de bloqueio da saída. O script de bloqueio, também conhecido como "scriptPubKey", especifica as regras que devem ser atendidas para que os fundos sejam gastos. A entrada inclui um script separado chamado "scriptSig", que fornece os dados necessários para satisfazer as condições do script de bloqueio.

As operações do Script do Bitcoin incluem operações matemáticas simples, operações de bits, operações criptográficas e ramificações condicionais. Ele é projetado intencionalmente para ser limitado em complexidade para garantir a segurança e evitar comportamentos maliciosos ou não intencionais. Essa escolha de design visa minimizar a superfície de ataque potencial e evitar vulnerabilidades que poderiam comprometer a integridade da rede Bitcoin.

PSBT

A Transação Parcialmente Assinada do Bitcoin (PSBT - Partially Signed Bitcoin Transaction, no original ) é um formato padronizado que permite que várias partes construam e assinem colaborativamente uma transação Bitcoin sem expor chaves privadas sensíveis. Ela é projetada para facilitar a assinatura offline, configurações de múltiplas assinaturas e integrações de carteira de hardware, tornando o processo de criação e assinatura de transações Bitcoin mais seguro e flexível.

A PSBT permite a separação do processo de criação da transação do processo de assinatura, o que é particularmente útil para cenários em que as partes desejam manter a segurança de suas chaves privadas enquanto colaboram na criação da transação.

Miniscript

O Miniscript é uma linguagem de script projetada para tornar mais fácil criar e analisar scripts do Bitcoin. O scripting tradicional do Bitcoin pode ser complexo e difícil de entender. O Miniscript visa simplificar esse processo, fornecendo uma maneira mais estruturada e legível por humanos de expressar condições de gasto.

Principais características do Miniscript:

Descritores: Os descritores são usados para definir as condições de gasto para saídas de uma maneira mais legível e abstrata, facilitando a criação e o gerenciamento de script complexos.

Política: A política é um mecanismo no Rust-Miniscript que determina se um descritor pode ser satisfeito ou não. Envolve a avaliação se as condições de gasto fornecidas foram atendidas e, se sim, permite o gasto do UTXO associado. Essa camada de abstração melhora ainda mais a legibilidade e a usabilidade do Rust-Miniscript, tornando-o acessível mesmo para aqueles sem um profundo entendimento do scripting do Bitcoin.

Compilador: O compilador faz a ponte entre os descritores legíveis por humanos e os scripts executáveis pela máquina. Ele recebe o descritor como entrada e gera o script Bitcoin correspondente, otimizando a seleção de opcodes e a montagem. Isso minimiza o potencial de erro e garante que os scripts resultantes sejam eficientes e seguros.

Taxa de Transação Bitcoin

Cada transação de Bitcoin é verificada por uma rede de nós e requer uma taxa de transação para processar a transação na blockchain.

No caso de transações de várias partes, é importante concordar com uma taxa de transação para evitar ataques de negação de serviço (DOS, na sigla em inglês). Para resolver esse problema, o rust-miniscript implementou a análise do pior caso absoluto para a satisfação de pesos e a utiliza para calcular as taxas.

Necessidade do Módulo de Planejamento

Já que sabemos que precisamos calcular a taxa de transação. Agora, imagine que, para transações de várias assinaturas (multisig), você precise de 2 de 3 chaves e tenha certeza de que uma das chaves não está presente. Então, você pode querer evitar calcular a taxa com base nessa chave ausente. Mas, de acordo com a implementação de pior caso para transações multisig de k de n chaves, as carteiras foram construídas de forma que, por padrão, foram necessárias n chaves, o que significa

Peso de satisfação = n * peso de cada chave

Para resolver esse problema de gastos excessivos em caso de falta de ativos, o módulo de planejamento desempenha um papel vital. Ele permite que você escolha um caminho de gasto. Portanto, agora você pode escolher apenas k chaves em vez de todas as n. Isso ajuda a reduzir as taxas para k * peso de cada chave. (k ≤ n)

O que é o Módulo de Planejamento

Um plano é representado por um caminho de gasto específico em um descritor (regras para gastar a saída da transação). É uma maneira de analisar a escolha de um caminho de gasto sem realmente produzir assinaturas ou outros dados de testemunha.

Para criar um plano de gasto, precisamos fornecer um descritor junto com chaves ou hashes de pré-imagem e restrições de bloqueio de tempo absolutas e relativas. Aqui também podemos determinar qual subconjunto das chaves disponíveis e hashes de pré-imagem realmente precisaremos usar. Isso é útil para construir uma transação de Bitcoin.

Uma vez que obtivemos as assinaturas necessárias, hashes de pré-imagem, etc., o plano de gastos pode criar uma testemunha ou script_sig para a entrada. Isso pode ser usado para assinar a transação e gastar a saída definida pelo descritor.

Em vez de construir a Testemunha [assinaturas, hashes de pré-imagem para verificar a propriedade], estão sendo construídos os Modelos de Testemunha, que contêm espaços reservados em vez de assinaturas e pré-imagens reais. Isso ajuda a determinar o peso da testemunha com mais precisão antes de construir a testemunha real.

Todo o fluxo de trabalho usando o módulo de Planejamento deve parecer como mostrado abaixo.

Fluxo de trabalho para o módulo de planejamento

https://github.com/rust-bitcoin/rust-miniscript/pull/481

Exemplo para entender o Miniscript e o Planejamento

Uma parte do meu trabalho foi escrever tutoriais de exemplo para os desenvolvedores que precisariam usar o Rust-Miniscript como uma biblioteca em suas carteiras. Para explicar todo o fluxo de trabalho do Miniscript e do Módulo de Planejamento, estou escrevendo um exemplo abaixo.

Explicação: Estamos tentando escrever uma política que tenha

chave A E [chave B OU chave C OU chave D]

Política de Gasto: and(pk(A),or(pk(B),or(pk(C),pk(D))))

Saída do Miniscript: and_v(or_c(pk(B),vc:or_i(pk_h(C),pk_h(D))),pk(A))

Script do Bitcoin:


<B> OP_CHECKSIG OP_NOTIF

  OP_IF

    OP_DUP OP_HASH160 <HASH160(C)> OP_EQUALVERIFY

  OP_ELSE

    OP_DUP OP_HASH160 <HASH160(D)> OP_EQUALVERIFY

  OP_ENDIF

  OP_CHECKSIGVERIFY

OP_ENDIF

<A> OP_CHECKSIG

Enter fullscreen mode Exit fullscreen mode

Peso de Satisfação e Módulo de Planejamento

Considerando essas chaves como parte de um descritor wsh que usa assinaturas ECDSA de comprimento 72 bytes, satisfazer esse script nos custaria aproximadamente 4 * 72 = 288 bytes. Mas pode haver outros custos adicionados para empurrar ou não satisfazer o script.

O descrito acima é o caso na Análise do Pior Caso.

Agora, com o módulo de planejamento, podemos escolher o nosso caminho. Por exemplo, os caminhos possíveis na política acima seriam

  1. chave A, chave B (144 bytes)

  2. chave A, chave C (144 bytes)

  3. chave A, chave D (144 bytes)

  4. chave A, chave B, chave C (216 bytes)

  5. chave A, chave B, chave D (216 bytes)

  6. chave A, chave C, chave D (216 bytes)

  7. chave A, chave B, chave C, chave D (288 bytes)

É óbvio que podemos escolher as chaves com o menor número de ativos, que neste caso é a combinação de duas chaves que custa 144 bytes. E podemos quase cortar pela metade a taxa escolhendo esse caminho de gasto e desenvolver a Entrada PSBT.

Isso é exatamente o que é possível com a ajuda do módulo de planejamento, escolhendo um caminho de gasto específico.

Como o trabalho foi feito em colaboração com os desenvolvedores do BDK, estamos aguardando a fusão do #481 e meu PR foi construído com base em seu trabalho. Abaixo está o PR que adiciona as APIs para obter todos os caminhos de gasto planejados possíveis e contar os ativos em tempo linear para verificar as possibilidades de ataques de negação de serviço (DOS - Denial of Service, no original).

https://github.com/rust-bitcoin/rust-miniscript/pull/559

Melhorias no Módulo de Planejamento

Trabalhei na lógica por trás da obtenção de todas as combinações de planos para um determinado descritor e política. Portanto, no exemplo acima, temos os 7 caminhos de gasto possíveis. Escrevi um método chamado get_all_assets que verifica recursivamente os diferentes elementos do Miniscript e retorna um vetor de Ativos que podem ser construídos a partir dos caminhos de política.

https://github.com/rust-bitcoin/rust-miniscript/pull/559

Para nos protegermos de Ataques de Negacão de Serviço (DOS), precisamos verificar o número total de planos. Se exceder um determinado limite, simplesmente rejeitaremos o descritor. Isso é importante porque, caso o algoritmo tente planejar a satisfação de todas as possibilidades, isso aumentaria a congestão na rede e não queremos que isso aconteça.

Temos duas funções diferentes para obter os ativos e contar todos os ativos, porque a obtenção de ativos ocorre em uma complexidade exponencial, mas a contagem simples de ativos deve levar menos tempo se já pudermos calcular a formulação combinatória para os elementos do Miniscript.

Por exemplo: AND(A, B) forneceria apenas 1 solução [(chave A, chave B)]

OR(A, B) forneceria 2 soluções [(chave A), (chave B)]

multi(k, A1, A2, A3,...,An) forneceria nCk possibilidades.

Desafios

Quando o Miniscript tenta finalizar o PSBT, ele não possui o descritor completo (que continha um fragmento pkh()) e, em vez disso, recorre à análise da raw script sig, que é traduzida internamente em um "expr_raw_pkh". Introduzimos o método substitute_raw_pkh para converter o expr_raw_pkh em pkh examinando as chaves disponíveis e seus hashes armazenados dentro do Hashmap da Entrada PSBT. Se a chave for encontrada correspondendo ao hash fornecido, ela será atualizada para pkh.

Para assinaturas ECDSA em Derivações BIP32, obtemos todas as chaves incluídas para um descritor dado. E para Schorr Sigs em Taproot, entramos em tap_key_origins. Aqui, precisamos de todas as chaves para fornecer um veredicto sobre se desejamos satisfazê-las ou não satisfazê-las. Essas chaves são mapeadas com seus hashes de chave e simplesmente substituímos os valores expr_raw_pkh por PKH(chave) para torná-lo de volta à forma de não hash. Isso reverte a tradução do descritor e podemos recuperar sua propriedade e finalizá-lo para a transação.

https://github.com/rust-bitcoin/rust-miniscript/pull/557

https://github.com/rust-bitcoin/rust-miniscript/pull/589

Conclusão

O módulo de planejamento no Rust-Miniscript revoluciona a maneira como os desenvolvedores criam scripts do Bitcoin, oferecendo uma abordagem de alto nível e legível por humanos para definir condições de gasto. Essa abstração aprimora a segurança, a legibilidade e a eficiência, tornando-a uma ferramenta inestimável para criar cenários de gasto complexos enquanto minimiza o risco de erros de scripting. Incorporar o módulo de planejamento do Rust-Miniscript em seus projetos pode simplificar significativamente o processo de construção de transações complexas de Bitcoin.

Se você estiver interessado em maximizar o potencial do Rust-Miniscript, explorar o módulo de planejamento é um passo essencial em sua jornada para se tornar um desenvolvedor proficiente em scripts do Bitcoin. Por favor, considere contribuir para este projeto incrível. Você não deve ser restringido pelo conhecimento limitado de Bitcoin. Se você conhece os conceitos básicos de programação e um pouco da linguagem Rust, sempre pode encontrar questões adequadas para iniciantes e torná-las úteis para o projeto. 😉


Este artigo foi escrito por Harshil Jani e traduzido por Adriano P. de Araujo. O original em inglês pode ser encontrado aqui

Top comments (0)