Learning-Bitcoin-from-the-C.../pt/06_1_Sending_a_Transaction_to_a_Multisig.md
2021-09-21 10:59:50 -03:00

17 KiB
Raw Permalink Blame History

6.1: Enviando uma Transação com Multisig

A primeira maneira de variar a forma como enviamos uma transação básica é usando um multisig. Isso nos dá a capacidade de exigir que várias pessoas (ou, ao menos, várias chaves privadas) autorizem o uso dos fundos.

Entendendo como Funcionam os Multisigs

Em uma transação P2PKH ou SegWit padrão, os bitcoins são enviados para um endereço baseado na chave pública, o que significa que a chave privada relacionada é necessária para desbloquear a transação, resolvendo o quebra-cabeça criptográfico e permitindo que reutilizemos o saldo. Mas e se pudéssemos bloquear uma transação com múltiplas chaves privadas? Isso efetivamente permitiria que os fundos fossem enviados a um grupo de pessoas, onde todas teriam que concordar em reutilizar o saldo.

📖 O que é uma multi-assinatura? Uma multi-assinatura é uma metodologia que permite que mais de uma pessoa crie uma assinatura digital em conjunto. É uma técnica geral para o uso criptográfico de chaves que vai muito além do Bitcoin.

Tecnicamente, um quebra-cabeça criptográfico com várias assinaturas é criado pelo Bitcoin usando o comando OP_CHECKMULTISIG e, normalmente, é encapsulado em um endereço P2SH. A seção §10.4: Programando um Multisig irá detalhar como isso funciona com mais precisão. Por enquanto, tudo que precisamos saber é que podemos usar o comando bitcoin-cli para criar endereços multi-assinatura. Os fundos podem ser enviados para esses endereços como qualquer endereço P2PKH ou Segwit normal, mas várias chaves privadas serão necessárias para que o saldo seja resgatado.

📖 O que é uma transação multisig? Uma transação multisig é uma transação no Bitcoin enviada para um endereço com várias assinaturas, exigindo assim que certas pessoas do grupo multi-assinatura precisem assinar a transação para poder reutilizar o saldo.

Multisigs simples exigem que todos no grupo assinem o UTXO quando for gasto. No entanto, há mais complexidade possível. Multisigs são geralmente descritos como sendo "m de n". Isso significa que a transação está trancada com um grupo de chaves "n", mas apenas "m" delas são necessárias para desbloquear a transação.

📖 O que é um multisg m-de-n? Em um multisig, "m" assinaturas de um grupo de "n" são necessárias para formar a assinatura, onde "m ≤ n".

Criando um Endereço Multisig

Para trancar um UTXO com várias chaves privadas, devemos primeiro criar um endereço multi-assinatura. Os exemplos usados aqui mostram a criação (e uso) de um multisig 2-de-2.

Criando os Endereços

Para criar um endereço multisig, devemos primeiro preparar os endereços que o multisig irá combinar. A prática recomendada sugere que sempre criemos novos endereços. Isso significa que cada participante irá executar o comando getnewaddress em sua própria máquina:

machine1$ address1=$(bitcoin-cli getnewaddress)

E:

machine2$ address2=$(bitcoin-cli getnewaddress)

Posteriormente, um dos destinatários (ou talvez algum terceiro) precisará combinar ambos endereços.

Coletando Chaves Públicas

No entanto, não podemos criar um multisig com os endereços, pois esses são os hashes das chaves públicas: Ao invés disso, precisamos das próprias chaves públicas.

Esta informação está disponível facilmente com o comando getaddressinfo.

Na máquina remota, que assumimos aqui que é machine2, podemos obter as informações em uma lista.

machine2$ bitcoin-cli -named getaddressinfo address=$address2
{
  "address": "tb1qr2tkjh8rs9xn5xaktf5phct0wxqufplawrfd9q",
  "scriptPubKey": "00141a97695ce3814d3a1bb65a681be16f7181c487fd",
  "ismine": true,
  "solvable": true,
  "desc": "wpkh([fe6f2292/0'/0'/1']02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3)#zc64l8dw",
  "iswatchonly": false,
  "isscript": false,
  "iswitness": true,
  "witness_version": 0,
  "witness_program": "1a97695ce3814d3a1bb65a681be16f7181c487fd",
  "pubkey": "02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3",
  "ischange": false,
  "timestamp": 1592957904,
  "hdkeypath": "m/0'/0'/1'",
  "hdseedid": "1dc70547f2b80e9bb5fde5f34fb3d85f8d8d1dab",
  "hdmasterfingerprint": "fe6f2292",
  "labels": [
    ""
  ]
}

O endereço pubkey ( 02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3) é o que precisamos. Vamos copiá-lo para nossa máquina local por qualquer meio que acharmos mais eficiente e que seja menos sujeito a erros.

Este processo precisa ser realizado para cada endereço de uma máquina diferente daquela onde o multisig está sendo construído. Obviamente, se algum terceiro estiver criando o endereço, precisaremos fazer isso para cada endereço.

⚠️ ATENÇÃO: O uso de hashes de chave pública pelo Bitcoin como endereços, ao invés de chaves públicas, na verdade representa uma camada adicional de segurança. Portanto, o envio de uma chave pública aumenta ligeiramente a vulnerabilidade do endereço associado, devido a alguma possibilidade do comprometimento da curva elíptica em um futuro distante. Não devemos nos preocupar em termos que enviar ocasionalmente uma chave pública para um uso como esse, mas devemos estar cientes de que os hashes de chave pública representam segurança e, portanto, as chaves públicas reais não devem ser enviadas de qualquer jeito para qualquer pessoa.

Se um dos endereços foi criado em nossa máquina local, que assumimos aqui que seja machine1, podemos simplesmente colocar o endereço pubkey em uma nova variável.

machine1$ pubkey1=$(bitcoin-cli -named getaddressinfo address=$address1 | jq -r '.pubkey')

Criando o Endereço

Um multisig agora pode ser criado com o comando createmultisig:

machine1$ bitcoin-cli -named createmultisig nrequired=2 keys='''["'$pubkey1'","02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3"]'''
{
  "address": "2N8MytPW2ih27LctLjn6LfLFZZb1PFSsqBr",
  "redeemScript": "522102da2f10746e9778dd57bd0276a4f84101c4e0a711f9cfd9f09cde55acbdd2d1912102bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa352ae",
  "descriptor": "sh(multi(2,02da2f10746e9778dd57bd0276a4f84101c4e0a711f9cfd9f09cde55acbdd2d191,02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3))#0pazcr4y"
}

⚠️ AVISO DE VERSÃO: Algumas versões do createmultisig permitem a entrada de chaves públicas ou endereços, algumas requerem apenas as chaves públicas. Atualmente, ambas parecem funcionar.

Ao criar o endereço multisig, listamos quantas assinaturas são necessárias com o argumento nrequired (que é o "m" em um multisig "m-de-n"), então listamos o conjunto total de assinaturas possíveis com a argumento keys (que é o "n"). Observe que as entradas keys provavelmente vieram de lugares diferentes. Nesse caso, incluímos $pubkey1 da máquina local e 02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3 de uma máquina remota.

NOTA M-DE-N VS N-DE-N: Este exemplo mostra a criação de um multisig simples 2-de-2 . Se quisermos criar uma assinatura m-de-n onde "m < n", precisamos apenas ajustar o campo nrequired e/ou o número de assinaturas keys no objeto JSON. Para um multisig 1-de-2, seria definido como nrequired = 1 e também listaria duas chaves, enquanto para um multisig 2-de-3, seria necessário um nrequired = 2, mas adicionaria mais uma chave pública à lista de keys.

Quando usado corretamente, o createmultisig retorna três resultados, todos criticamente importantes.

O endereço é o que iremos distribuir para as pessoas que desejam enviar os fundos. Podemos notar que ele tem um novo prefixo 2, exatamente como os endereços P2SH-SegWit. Isso porque, como eles, o comando createmultisig está na verdade criando um tipo de endereço totalmente novo chamado endereço P2SH. Ele funciona exatamente como um endereço P2PKH padrão para envio de fundos, mas como este foi criado para exigir vários endereços, precisaremos trabalhar um pouco mais para utilizá-los.

🔗 TESTNET vs MAINNET: Na testenet, o prefixo para endereços P2SH é 2, enquanto na Mainnet é 3.

O redeemScript é o que precisaremos para resgatar os fundos (junto com as chaves privadas de "m" dos "n" endereços). Este script é outro recurso especial dos endereços P2SH e será explicado com mais detalhes na seção §10.3: Rodando um Script no Bitcoin com P2SH. Por enquanto, precisamos apenas saber que alguns dados são necessários para podemos gastar nosso dinheiro.

O descritor é a descrição padronizada para um endereço que encontramos na seção §3.5: Compreendendo o Descritor. Ele fornece uma maneira de importar esse endereço de volta para a outra máquina, usando o RPC importmulti.

📖 O que é um endereço P2SH? O P2SH significa Pay-To-Script-Hash. É um tipo de destinatário diferente de um endereço P2PKH padrão ou mesmo de um Bech32, usado para fundos cujo resgate é baseado em scripts de Bitcoin mais complexos. O bitcoin-cli usa o encapsulamento P2SH para ajudar a padronizar e simplificar os multisigs como "multisigs P2SH", assim como P2SH-SegWit estava usando o P2SH para padronizar os endereços SegWit e torná-los totalmente compatíveis com as versões antigas.

⚠️ ATENÇÃO: endereços P2SH multisig, como os criados pelo bitcoin-cli, têm um limite para "m" e "n" nos multisigs com base no tamanho máximo do script de resgate, que atualmente é de 520 bytes. Praticamente, não vamos chegar a isso, a menos que estejamos nos excedendo em algo.

Salvando Nosso Trabalho

Aqui está uma informação importante: nada sobre nosso multisig é salvo em nossa carteira usando essas técnicas básicas. Para resgatar o saldo enviado para este endereço multisig no futuro, precisaremos reter duas informações cruciais:

  • Uma lista dos endereços Bitcoin usados no multisig, e;
  • A saída redeemScript criada pelo comando createmultsig.

Tecnicamente, o redeemScript pode ser recriado executando novamente o createmultisig com a lista completa de chaves públicas na mesma ordem e com a contagem m-de-n correta. Mas, é melhor agarrar-se a ela e evitar qualquer tipo de estresse futuro.

Observe a Ordem

Aqui está mais uma coisa que devemos tomar muito cuidado: a ordem importa. A ordem das chaves usadas para criar um multisig cria um hash único, ou seja, se colocarmos as chaves em uma ordem diferente, elas irão produzir um endereço diferente, conforme mostrado abaixo:

$ bitcoin-cli -named createmultisig nrequired=2 keys='''["'$pubkey1'","'$pubkey2'"]'''
{
  "address": "2NFBQvz57UzKWDr2Vx5D667epVZifjGixkm",
  "redeemScript": "52210342b306e410283065ffed38c3139a9bb8805b9f9fa6c16386e7ea96b1ba54da0321039cd6842869c1bfec13cfdbb7d8285bc4c501d413e6633e3ff75d9f13424d99b352ae",
  "descriptor": "sh(multi(2,0342b306e410283065ffed38c3139a9bb8805b9f9fa6c16386e7ea96b1ba54da03,039cd6842869c1bfec13cfdbb7d8285bc4c501d413e6633e3ff75d9f13424d99b3))#8l6hvjsk"
}
standup@btctest20:~$ bitcoin-cli -named createmultisig nrequired=2 keys='''["'$pubkey2'","'$pubkey1'"]'''
{
  "address": "2N5bC4Yc5Pqept1y8nPRqvWmFSejkVeRb1k",
  "redeemScript": "5221039cd6842869c1bfec13cfdbb7d8285bc4c501d413e6633e3ff75d9f13424d99b3210342b306e410283065ffed38c3139a9bb8805b9f9fa6c16386e7ea96b1ba54da0352ae",
  "descriptor": "sh(multi(2,039cd6842869c1bfec13cfdbb7d8285bc4c501d413e6633e3ff75d9f13424d99b3,0342b306e410283065ffed38c3139a9bb8805b9f9fa6c16386e7ea96b1ba54da03))#audl88kg"
}

Mais notavelmente, cada ordem cria um redeemScript diferente. Isso significa que se usamos essas técnicas básicas e não salvarmos o redemScript conforme as instruções, teremos que percorrer um número cada vez maior de variações para encontrar a correta quando tentarmos gastar os fundos!

O BIP67 sugere uma maneira de ordenar lexicograficamente as chaves, de modo que sempre gerem as mesmas multisigs. A ColdCard e a Electrum estão entre as carteiras que já possuem suporte a isso. Claro, isso pode causar problemas por si só se não soubermos se um endereço multisig foi criado com chaves classificadas ou não classificadas. Mais uma vez, os descritores vêm ao nosso resgate. Se um multisig não for classificado, ele será construído com a função multi, e se for classificado, será construído com a função sortedmulti.

Se olharmos o desc do multisig que criamos acima, veremos que o Bitcoin Core atualmente não classifica os multisigs:

  "descriptor": "sh(multi(2,02da2f10746e9778dd57bd0276a4f84101c4e0a711f9cfd9f09cde55acbdd2d191,02bfde48be4aa8f4bf76c570e98a8d287f9be5638412ab38dede8e78df82f33fa3))#0pazcr4y"

Porém, se ele importa um endereço do tipo sortedmulti, ele fará o processo correto, que é o ponto principal dos descritores!

⚠️ AVISO DE VERSÃO: O Bitcoin Core só entende a função do descritor sortedmulti após a versão 0.20.0. Podemos tentar acessar o descritor em uma versão anterior do Bitcoin Core e obteremos um erro como A function is needed within P2WSH.

Enviando para um Endereço Multisig

Se tivermos um multisig em um formato P2SH conveniente, como o gerado pelo bitcoin-cli, podemos enviar como um endereço normal.

$ utxo_txid=$(bitcoin-cli listunspent | jq -r '.[0] | .txid') 
$ utxo_vout=$(bitcoin-cli listunspent | jq -r '.[0] | .vout')
$ recipient="2N8MytPW2ih27LctLjn6LfLFZZb1PFSsqBr"

$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$recipient'": 0.000065}''')
$ bitcoin-cli -named decoderawtransaction hexstring=$rawtxhex 
{
  "txid": "b164388854f9701051809eed166d9f6cedba92327e4296bf8a265a5da94f6521",
  "hash": "b164388854f9701051809eed166d9f6cedba92327e4296bf8a265a5da94f6521",
  "version": 2,
  "size": 83,
  "vsize": 83,
  "weight": 332,
  "locktime": 0,
  "vin": [
    {
      "txid": "c6de60427b28d8ec8102e49771e5d0348fc3ef6a5bf02eb864ec745105a6951b",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 0.00006500,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_HASH160 a5d106eb8ee51b23cf60d8bd98bc285695f233f3 OP_EQUAL",
        "hex": "a914a5d106eb8ee51b23cf60d8bd98bc285695f233f387",
        "reqSigs": 1,
        "type": "scripthash",
        "addresses": [
          "2N8MytPW2ih27LctLjn6LfLFZZb1PFSsqBr"
        ]
      }
    }
  ]
}

$ signedtx=$(bitcoin-cli -named signrawtransactionwithwallet hexstring=$rawtxhex | jq -r '.hex')
$ bitcoin-cli -named sendrawtransaction hexstring=$signedtx
b164388854f9701051809eed166d9f6cedba92327e4296bf8a265a5da94f6521

Como podemos ver, não houve nada de incomum na criação da transação e ela parece normal, embora com um endereço com um prefixo diferente do normal (2N8MytPW2ih27LctLjn6LfLFZZb1PFSsqBr). Sem surpresa, como também não vimos diferença quando enviamos para endereços Bech32 pela primeira vez na seção §4.6.

Resumo: Enviando uma Transação com Multisig

Os endereços multisig trancam os fundos usando várias chaves privadas, possivelmente exigindo todas essas chaves privadas para resgate e, possivelmente, exigindo apenas algumas do conjunto. Eles são fáceis de serem criados com o bitcoin-cli e são simples para serem enviados. Essa facilidade se deve em grande parte ao uso invisível de endereços P2SH (Pay-To-Script-Hash), um tópico extenso que já falamos duas vezes, com endereços P2SH-SegWit e agora com os multisigs, e um outro que receberá mais atenção no futuro.

🔥 Qual é o poder das multi-assinaturas? As multi-assinaturas permitem a modelagem de uma variedade de arranjos financeiros, como corporações, parcerias, comitês e outros grupos. Um multisig 1-de-2 pode ser a conta bancária conjunta de um casal, enquanto um multisig 2-de-2 pode ser usado para grandes despesas por uma parceria de responsabilidade limitada. Os multisigs também constituem uma das bases dos Smart Contracts. Por exemplo, um negócio imobiliário pode ser fechado com um multisig 2-de-3, onde as assinaturas são enviadas pelo comprador, pelo vendedor e por um agente de custódia. Depois que o agente de custódia concorda que todas as condições foram atendidas, ele libera os fundos para o vendedor, ou, alternativamente, o comprador e o vendedor podem liberar os fundos em conjunto.

O Que Vem Depois?

Vamos continuar "Expandindo Transações no Bitcoin com Multisigs" na seção §6.2: Gastando uma Transação com Multisig.