mirror of
https://github.com/ChristopherA/Learning-Bitcoin-from-the-Command-Line.git
synced 2025-06-07 16:06:26 +00:00
Merge pull request #264 from KoreaComK/chapter07
Chapter 07 Translated by @koreacomk reviewed by @namcios
This commit is contained in:
commit
92c1994ba7
24
pt/07_0_Expanding_Bitcoin_Transactions_PSBTs.md
Normal file
24
pt/07_0_Expanding_Bitcoin_Transactions_PSBTs.md
Normal file
@ -0,0 +1,24 @@
|
||||
# Capítulo Sete: Expandindo Transações no Bitcoin com PSBTs
|
||||
|
||||
No capítulo anterior, discutimos como usar os multisigs para determinar colaborativamente o consentimento entre várias partes. Essa não é a única forma de colaborar na criação de transações no Bitcoin. Os PSBTs são uma tecnologia muito mais recente que permite que colaboremos em vários estágios, incluindo a criação, financiamento e autenticação de uma transação Bitcoin.
|
||||
|
||||
## Objetivos para Esta Seção
|
||||
|
||||
Depois de trabalhar neste capítulo, um desenvolvedor será capaz de:
|
||||
|
||||
* Criar transações com PSBTs;
|
||||
* Usas ferramentas da linha de comando para completar PSBTs;
|
||||
* Usar o HWI para interagir com uma hardware wallet.
|
||||
|
||||
Os objetivos secundários do capítulo incluem a capacidade de:
|
||||
|
||||
* Entender como os PSBTs diferem dos multisig;
|
||||
* Compreender completamente o fluxo de trabalho com os PSBTs;
|
||||
* Planejar para todo o poder dos PSBTs;
|
||||
* Compreender o uso de uma hardware wallet.
|
||||
|
||||
## Tabela de Conteúdo
|
||||
|
||||
* [Seção Um: Criando uma Transação Bitcoin Parcialmente Assinada](07_1_Creating_a_Partially_Signed_Bitcoin_Transaction.md)
|
||||
* [Seção Dois: Usando uma Transação Bitcoin Parcialmente Assinada](07_2_Using_a_Partially_Signed_Bitcoin_Transaction.md)
|
||||
* [Seção Três: Integrando com Hardware Wallets](07_3_Integrating_with_Hardware_Wallets.md)
|
514
pt/07_1_Creating_a_Partially_Signed_Bitcoin_Transaction.md
Normal file
514
pt/07_1_Creating_a_Partially_Signed_Bitcoin_Transaction.md
Normal file
@ -0,0 +1,514 @@
|
||||
# 7.1: Criando uma Transação Bitcoin Parcialmente Assinada
|
||||
|
||||
> :information_source: **NOTA:** Esta seção foi adicionada recentemente ao curso e é um rascunho inicial que ainda pode estar aguardando revisão.
|
||||
|
||||
As Transações Bitcoin Parcialmente Assinadas (Partially Signed Bitcoin Transactions ou mais conhecidas como PSBTs) são a forma mais recente de variar a criação de transações básicas do Bitcoin. Eles fazem isso introduzindo a colaboração em todas as etapas do processo, permitindo que as pessoas (ou sistemas) não apenas autentiquem as transações em conjunto (como nas multisigs), mas também criem, financiem e transmitam facilmente de forma colaborativa.
|
||||
|
||||
> :aviso: **AVISO DE VERSÃO:** Esta é uma inovação do Bitcoin Core v 0.17.0. As versões anteriores do Bitcoin Core não funcionarão com o PSBT enquanto estiver em andamento (apesar de conseguir reconhecer a transação final). Algumas atualizações e upgrades para PSBTs continuaram até a versão 0.20.0.
|
||||
|
||||
## Entendendo Como PSBTs Funcionam
|
||||
|
||||
Os multisigs são ótimos para o caso específico de manter fundos em conjunto e definir regras para quem, entre os signatários conjuntos, poderia autenticar o uso do saldo. Existem muitos casos de uso, como: uma conta conjunta entre cônjuges (uma assinatura 1-de-2); um requisito fiduciário para duplo controle (uma assinatura 2-de-2); e um depósito (uma assinatura 2-de-3).
|
||||
|
||||
> :book: ***O que é uma PSBT?*** Como o nome sugere, uma PSBT é uma transação que não foi totalmente assinada. Isso é importante porque, uma vez que uma transação é assinada, seu conteúdo está travado. O [BIP174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) definiu uma metodologia abstrata para colocar as PSBTs junto que descreve e padroniza funções de criação colaborativa. Um *Criador* propõe uma transação; um ou mais *Atualizadores* a complementam; e um ou mais *Assinantes* a autenticam; antes de um *Finalizador* concluir o processo; e um *Extrator* o transformar em uma transação na rede Bitcoin. Também pode haver um *Combinador* que mescla as PSBTs paralelas de vários usuários.
|
||||
|
||||
As PSBTs podem, inicialmente, ter a mesma aparência que os multisigs porque têm um único pedaço de funcionalidade sobreposto: a capacidade de assinar uma transação em conjunto. No entanto, elas foram criadas para um caso de uso totalmente diferente. As PSBTs reconhecem a necessidade de vários programas criarem uma transação em conjunto por vários motivos diferentes e fornecem um formato regularizado para isso. Elas são especialmente úteis para casos de uso envolvendo hardware wallets (veja a seção [§7.3](https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line/blob/master/07_3_Integrating_with_Hardware_Wallets.md )), que são protegidas contra acesso total à internet e tendem a ter um histórico mínimo, quando não zero, de transações.
|
||||
|
||||
Em geral, as PSBTs fornecem vários elementos funcionais que aprimoram esse caso de uso:
|
||||
|
||||
1. Elas fornecem um _padrão_ para criar transações de forma colaborativa, enquanto as metodologias anteriores (incluindo o multisig do capítulo anterior) dependiam de implementação;
|
||||
2. Elas oferecem suporte a uma _variedade mais ampla de casos de uso_, incluindo financiamento simples em conjunto;
|
||||
3. Elas suportam _hardware wallets_ e outros casos em que um node pode não ter um histórico completo das transações;
|
||||
4. Elas opcionalmente permitem a combinação de _transações não serializadas_, não exigindo que um código hexadecimal cada vez maior seja passado de usuário para usuário.
|
||||
|
||||
As PSBTs fazem seu trabalho complementando as informações normais da transação com um conjunto de entradas e saídas, cada uma delas definindo tudo o que precisamos saber sobre os UTXOs, de forma que mesmo uma carteira com airgap possa tomar uma decisão informada sobre as assinaturas. Assim, uma entrada lista o saldo de um UTXO e o que precisa ser feito para gastá-lo, enquanto a saída faz o mesmo para os UTXOs que ela está criando.
|
||||
|
||||
Esta primeira seção descreverá o processo PSBT padrão com as seguintes funções: Criador, Atualizador, Assinante, Finalizador, Extrator. Faremos isso em uma máquina só, o que irá parecer uma forma complicada de criar uma transação bruta. Mas, tenha fé, há um motivo para isso! Nas seções [§7.2](07_2_Using_a_Partially_Signed_Bitcoin_Transaction.md) e [§7.3](07_3_Integrating_with_Hardware_Wallets.md) vamos ver alguns exemplos reais de como utilizar as PSBTs e vamos transformar este sistema simples em um processo colaborativo compartilhado entre várias máquinas que tem efeitos reais e cria oportunidades reais.
|
||||
|
||||
## Criando uma PSBT à Moda Antiga
|
||||
|
||||
#### Função na PSBT: Criador
|
||||
|
||||
A maneira mais fácil de criar uma PSBT é pegando uma transação existente e usando o comando ```converttopsbt``` para transformá-la em uma PSBT. Certamente, esta não é a _melhor_ maneira, uma vez que requer que façamos uma transação usando um formato (uma transação bruta) e depois convertendo-a para outro (PSBT), mas se tivermos um software antigo que só pode gerar uma transação bruta, pode ser que precisemos utilizá-lo.
|
||||
|
||||
Apenas criamos uma transação bruta normalmente:
|
||||
|
||||
```
|
||||
$ utxo_txid_1=$(bitcoin-cli listunspent | jq -r '.[0] | .txid')
|
||||
$ utxo_vout_1=$(bitcoin-cli listunspent | jq -r '.[0] | .vout')
|
||||
$ utxo_txid_2=$(bitcoin-cli listunspent | jq -r '.[1] | .txid')
|
||||
$ utxo_vout_2=$(bitcoin-cli listunspent | jq -r '.[1] | .vout')
|
||||
$ echo $utxo_txid_1 $utxo_vout_1 $utxo_txid_2 $utxo_vout_2
|
||||
c6de60427b28d8ec8102e49771e5d0348fc3ef6a5bf02eb864ec745105a6951b 1 8748eff5f12ca886e3603d9e30227dcb3f0332e0706c4322fec96001f7c7f41c 0
|
||||
$ recipient=tb1qcaedd724gts3aug73m78c7nfsv9d8zs9q6h2kd
|
||||
$ rawtxhex=$(bitcoin-cli -named createrawtransaction inputs='''[ { "txid": "'$utxo_txid_1'", "vout": '$utxo_vout_1' }, { "txid": "'$utxo_txid_2'", "vout": '$utxo_vout_2' } ]''' outputs='''{ "'$recipient'": 0.0000065 }''')
|
||||
```
|
||||
Então agora, a convertemos:
|
||||
```
|
||||
$ psbt=$(bitcoin-cli -named converttopsbt hexstring=$rawtxhex)
|
||||
$ echo $psbt
|
||||
cHNidP8BAHsCAAAAAhuVpgVRdOxkuC7wW2rvw4800OVxl+QCgezYKHtCYN7GAQAAAAD/////HPTH9wFgyf4iQ2xw4DIDP8t9IjCePWDjhqgs8fXvSIcAAAAAAP////8BigIAAAAAAAAWABTHctb5VULhHvEejvx8emmDCtOKBQAAAAAAAAAA
|
||||
```
|
||||
É possível notar que a codificação PSBT é muito diferente da transação hexadecimal que estamos acostumados.
|
||||
|
||||
Mas, se pudermos, optaremos por criar o PSBT diretamente.
|
||||
|
||||
## Criando uma PSBT da Maneira Difícil
|
||||
|
||||
#### Função na PSBT: Criador
|
||||
|
||||
A primeira metodologia de criação de uma PSBT sem passar por outro formato é usando o comando para PSBT análogo ao ```createrawtransaction```. Usando o ```createpsbt``` teremos o controle máximo a custo do máximo trabalho e da oportunidade máxima de erros.
|
||||
|
||||
A CLI deve parecer bastante familiar, a diferença é que agora usamos um novo comando RPC:
|
||||
```
|
||||
$ psbt_1=$(bitcoin-cli -named createpsbt inputs='''[ { "txid": "'$utxo_txid_1'", "vout": '$utxo_vout_1' }, { "txid": "'$utxo_txid_2'", "vout": '$utxo_vout_2' } ]''' outputs='''{ "'$recipient'": 0.0000065 }''')
|
||||
```
|
||||
A equipe do Bitcoin Core certificou-se de que ```createpsbt``` funcionasse de maneira muito parecida com a```createrawtransaction```, portanto não precisamos aprender uma nova sintaxe diferente.
|
||||
|
||||
Podemos verificar se a nova PSBT é a mesma criada pelo ```converttopsbt```:
|
||||
```
|
||||
$ echo $psbt_1
|
||||
cHNidP8BAHsCAAAAAhuVpgVRdOxkuC7wW2rvw4800OVxl+QCgezYKHtCYN7GAQAAAAD/////HPTH9wFgyf4iQ2xw4DIDP8t9IjCePWDjhqgs8fXvSIcAAAAAAP////8BigIAAAAAAAAWABTHctb5VULhHvEejvx8emmDCtOKBQAAAAAAAAAA
|
||||
$ if [ "$psbt" == "$psbt_1" ]; then echo "PSBTs are equal"; else echo "PSBTs are not equal"; fi
|
||||
PSBTs are equal
|
||||
```
|
||||
|
||||
## Examinando uma PSBT
|
||||
|
||||
#### Função na PSBT: Qualquer Uma
|
||||
|
||||
Então, como a nossa PSBT realmente se parece? Podemos ver isso com o comando ```decodepsbt```:
|
||||
```
|
||||
$ bitcoin-cli -named decodepsbt psbt=$psbt
|
||||
{
|
||||
"tx": {
|
||||
"txid": "ea73a631b456d2b041ed73bf5767946408c6ff067716929a68ecda2e3e4de6d3",
|
||||
"hash": "ea73a631b456d2b041ed73bf5767946408c6ff067716929a68ecda2e3e4de6d3",
|
||||
"version": 2,
|
||||
"size": 123,
|
||||
"vsize": 123,
|
||||
"weight": 492,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "c6de60427b28d8ec8102e49771e5d0348fc3ef6a5bf02eb864ec745105a6951b",
|
||||
"vout": 1,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967295
|
||||
},
|
||||
{
|
||||
"txid": "8748eff5f12ca886e3603d9e30227dcb3f0332e0706c4322fec96001f7c7f41c",
|
||||
"vout": 0,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.00000650,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 c772d6f95542e11ef11e8efc7c7a69830ad38a05",
|
||||
"hex": "0014c772d6f95542e11ef11e8efc7c7a69830ad38a05",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"tb1qcaedd724gts3aug73m78c7nfsv9d8zs9q6h2kd"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"unknown": {
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
},
|
||||
{
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
É importante notar que, embora tenhamos definido os fundamentos da transação: os ```vins``` de onde o dinheiro está vindo e os ```vouts``` para onde está indo, ainda não definimos os ```inputs``` e os ```outputs``` que são o coração da PSBT e que são necessários para os usuários offline avaliá-las. Isso já é esperado: o papel do Criador conforme definido no [BIP174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) é delinear a transação, enquanto o papel do Atualizador é começar a preencher os dados específicos da PSBT. (Outros comandos combinam as funções de Criador e Atualizador, mas o ```createpsbt``` não, porque não tem acesso à nossa carteira).
|
||||
|
||||
Também podemos usar o comando ```analyzepsbt``` para verificar nosso estado atual:
|
||||
```
|
||||
standup@btctest20:~$ bitcoin-cli -named analyzepsbt psbt=$psbt
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"has_utxo": false,
|
||||
"is_final": false,
|
||||
"next": "updater"
|
||||
},
|
||||
{
|
||||
"has_utxo": false,
|
||||
"is_final": false,
|
||||
"next": "updater"
|
||||
}
|
||||
],
|
||||
"next": "updater"
|
||||
}
|
||||
```
|
||||
Da mesma forma, o comando ```analysepsbt``` nos mostra uma PSBT que precisa ser trabalhada. Damos uma olhada em cada um dos dois `inputs` (correspondendo aos dois ```vins```), e nenhum deles possui as informações de que precisamos.
|
||||
|
||||
## Finalizando uma PSBT
|
||||
|
||||
#### Funções na PSBT: Atualizador, Assinante e Finalizador
|
||||
|
||||
Existe o comando ```utxoupdatepsbt``` que pode ser usado para atualizar UTXOs, importando as informações do descritor manualmente, mas não queremos usá-lo a menos que tenhamos um caso de uso em que não tenhamos todas as informações nas carteiras de todos que irão assinar a PSBT.
|
||||
|
||||
> :information_source: **NOTA:** Se escolhermos Atualizar a PSBT com o ```utxoupdatepsbt```, ainda precisaremos usar o ```walletprocesspsbt``` para assiná-lo: é o único comando que a função Assinante possui em PSBTs que está disponível no `bitcoin-cli`.
|
||||
|
||||
Ao invés disso, devemos usar o ```walletprocesspsbt```, que irá atualizar, assinar e finalizar:
|
||||
```
|
||||
$ bitcoin-cli walletprocesspsbt $psbt
|
||||
{
|
||||
"psbt": "cHNidP8BAHsCAAAAAhuVpgVRdOxkuC7wW2rvw4800OVxl+QCgezYKHtCYN7GAQAAAAD/////HPTH9wFgyf4iQ2xw4DIDP8t9IjCePWDjhqgs8fXvSIcAAAAAAP////8BigIAAAAAAAAWABTHctb5VULhHvEejvx8emmDCtOKBQAAAAAAAQEfAQAAAAAAAAAWABRsRdOvqHYghsS9dtinGsfJduGRlgEIawJHMEQCIAqJbxz6dBzNpfaDu4XZXb+DbDkM3UWnhezh9UdmeVghAiBRxMlW2o0wEtphtUZRWIiJOaGtXfsQbB4lovkvE4eRIgEhArrDpkX9egpTfGJ6039faVBYxY0ZzrADPpE/Gpl14A3uAAEBH0gDAAAAAAAAFgAU1ZEJG4B0ojde2ZhanEsY7+z9QWUBCGsCRzBEAiB+sNNCO4xiFQ+DoHVrqqk9yM0V4H9ZSyExx1PW7RbjsgIgUeWkQ3L7aAv1xIe7h+8PZb8ECsXg1UzbtPW8wd2qx0UBIQKIO7VGPjfVUlLYs9XCFBsAezfIp9tiEfdclVrMXqMl6wAA",
|
||||
"complete": true
|
||||
}
|
||||
```
|
||||
Obviamente, precisaremos salvar as informações da ```psbt``` usando```JQ```:
|
||||
```
|
||||
$ psbt_f=$(bitcoin-cli walletprocesspsbt $psbt | jq -r '.psbt')
|
||||
```
|
||||
Podemos ver que os `inputs` foram preenchidos:
|
||||
```
|
||||
$ bitcoin-cli decodepsbt $psbt_f
|
||||
{
|
||||
"tx": {
|
||||
"txid": "ea73a631b456d2b041ed73bf5767946408c6ff067716929a68ecda2e3e4de6d3",
|
||||
"hash": "ea73a631b456d2b041ed73bf5767946408c6ff067716929a68ecda2e3e4de6d3",
|
||||
"version": 2,
|
||||
"size": 123,
|
||||
"vsize": 123,
|
||||
"weight": 492,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "c6de60427b28d8ec8102e49771e5d0348fc3ef6a5bf02eb864ec745105a6951b",
|
||||
"vout": 1,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967295
|
||||
},
|
||||
{
|
||||
"txid": "8748eff5f12ca886e3603d9e30227dcb3f0332e0706c4322fec96001f7c7f41c",
|
||||
"vout": 0,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.00000650,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 c772d6f95542e11ef11e8efc7c7a69830ad38a05",
|
||||
"hex": "0014c772d6f95542e11ef11e8efc7c7a69830ad38a05",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"tb1qcaedd724gts3aug73m78c7nfsv9d8zs9q6h2kd"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"unknown": {
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"witness_utxo": {
|
||||
"amount": 0.00000001,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 6c45d3afa8762086c4bd76d8a71ac7c976e19196",
|
||||
"hex": "00146c45d3afa8762086c4bd76d8a71ac7c976e19196",
|
||||
"type": "witness_v0_keyhash",
|
||||
"address": "tb1qd3za8tagwcsgd39awmv2wxk8e9mwryvktqmkkg"
|
||||
}
|
||||
},
|
||||
"final_scriptwitness": [
|
||||
"304402200a896f1cfa741ccda5f683bb85d95dbf836c390cdd45a785ece1f54766795821022051c4c956da8d3012da61b5465158888939a1ad5dfb106c1e25a2f92f1387912201",
|
||||
"02bac3a645fd7a0a537c627ad37f5f695058c58d19ceb0033e913f1a9975e00dee"
|
||||
]
|
||||
},
|
||||
{
|
||||
"witness_utxo": {
|
||||
"amount": 0.00000840,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 d591091b8074a2375ed9985a9c4b18efecfd4165",
|
||||
"hex": "0014d591091b8074a2375ed9985a9c4b18efecfd4165",
|
||||
"type": "witness_v0_keyhash",
|
||||
"address": "tb1q6kgsjxuqwj3rwhkenpdfcjccalk06st9z0k0kh"
|
||||
}
|
||||
},
|
||||
"final_scriptwitness": [
|
||||
"304402207eb0d3423b8c62150f83a0756baaa93dc8cd15e07f594b2131c753d6ed16e3b2022051e5a44372fb680bf5c487bb87ef0f65bf040ac5e0d54cdbb4f5bcc1ddaac74501",
|
||||
"02883bb5463e37d55252d8b3d5c2141b007b37c8a7db6211f75c955acc5ea325eb"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
}
|
||||
],
|
||||
"fee": 0.00000191
|
||||
}
|
||||
```
|
||||
Ou para ser mais preciso: (1) a PSBT foi atualizada com as informações de ```witness_utxo```; (2) a PSBT foi assinada; e (3) a PSBT foi finalizada.
|
||||
|
||||
## Criando uma PSBT da Maneira Fácil
|
||||
|
||||
#### Função na PSBT: Criador e Atualizador
|
||||
|
||||
Se esperamos que haja um comando equivalente ao ```fundrawtransaction```, ficaremos satisfeitos em saber que existe: ele é o ```walletcreatefundedpsbt```. Podemos usá-lo da mesma forma que ```createpsbt```:
|
||||
```
|
||||
$ bitcoin-cli -named walletcreatefundedpsbt inputs='''[ { "txid": "'$utxo_txid_1'", "vout": '$utxo_vout_1' }, { "txid": "'$utxo_txid_2'", "vout": '$utxo_vout_2' } ]''' outputs='''{ "'$recipient'": 0.0000065 }'''
|
||||
{
|
||||
"psbt": "cHNidP8BAOwCAAAABBuVpgVRdOxkuC7wW2rvw4800OVxl+QCgezYKHtCYN7GAQAAAAD/////HPTH9wFgyf4iQ2xw4DIDP8t9IjCePWDjhqgs8fXvSIcAAAAAAP/////uFwerANKjyVK6WaR7gzlX+lOf+ORsfjP5LYCSNIbhaAAAAAAA/v///4XjOeey0NyGpJYpszNWF8AFNiuFaWsjkOrk35Jp+9kKAAAAAAD+////AtYjEAAAAAAAFgAUMPsier2ey1eH48oGqrbbYGzNHgKKAgAAAAAAABYAFMdy1vlVQuEe8R6O/Hx6aYMK04oFAAAAAAABAR8BAAAAAAAAABYAFGxF06+odiCGxL122Kcax8l24ZGWIgYCusOmRf16ClN8YnrTf19pUFjFjRnOsAM+kT8amXXgDe4Q1gQ4AAAAAIABAACADgAAgAABAR9IAwAAAAAAABYAFNWRCRuAdKI3XtmYWpxLGO/s/UFlIgYCiDu1Rj431VJS2LPVwhQbAHs3yKfbYhH3XJVazF6jJesQ1gQ4AAAAAIABAACADAAAgAABAIwCAAAAAdVmsvkSBmfeHqNAe/wDCQ5lEp9F/587ftzCD1UL60nMAQAAABcWABRzFxRJfFPl8FJ6SxjAJzy3mCAMXf7///8CQEIPAAAAAAAZdqkUf0NzebzGbEB0XtwYkeprODDhl12IrMEwLQAAAAAAF6kU/d+kMX6XijmD+jWdUrLZlJUnH2iHPhQbACIGA+/e40wACf0XXzsgteWlUX/V0WdG8uY1tEYXra/q68OIENYEOAAAAACAAAAAgBIAAIAAAQEfE4YBAAAAAAAWABTVkQkbgHSiN17ZmFqcSxjv7P1BZSIGAog7tUY+N9VSUtiz1cIUGwB7N8in22IR91yVWsxeoyXrENYEOAAAAACAAQAAgAwAAIAAIgICKMavAB+71Adqsbf+XtC1g/OlmLEuTp3U0axyeu/LAI0Q1gQ4AAAAAIABAACAGgAAgAAA",
|
||||
"fee": 0.00042300,
|
||||
"changepos": 0
|
||||
}
|
||||
```
|
||||
No entanto, a grande vantagem é que podemos usá-lo para se autofinanciar, deixando de fora os ```inputs```, assim como o ```fundrawtransaction```.
|
||||
```
|
||||
$ psbt_new=$(bitcoin-cli -named walletcreatefundedpsbt inputs='''[]''' outputs='''{ "'$recipient'": 0.0000065 }''' | jq -r '.psbt')
|
||||
$ bitcoin-cli decodepsbt $psbt_new
|
||||
{
|
||||
"tx": {
|
||||
"txid": "9f2c6205ac797c1020f7f261e3ab71cd0699ff4b1a8934f68b273c71547e235f",
|
||||
"hash": "9f2c6205ac797c1020f7f261e3ab71cd0699ff4b1a8934f68b273c71547e235f",
|
||||
"version": 2,
|
||||
"size": 154,
|
||||
"vsize": 154,
|
||||
"weight": 616,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "8748eff5f12ca886e3603d9e30227dcb3f0332e0706c4322fec96001f7c7f41c",
|
||||
"vout": 0,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967294
|
||||
},
|
||||
{
|
||||
"txid": "68e1863492802df9337e6ce4f89f53fa5739837ba459ba52c9a3d200ab0717ee",
|
||||
"vout": 0,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967294
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.00971390,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 09a74ef0bae4d68b0b2ec9a7c4557a2b5c85bd8b",
|
||||
"hex": "001409a74ef0bae4d68b0b2ec9a7c4557a2b5c85bd8b",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"tb1qpxn5au96untgkzewexnug4t69dwgt0vtfahcv6"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00000650,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 c772d6f95542e11ef11e8efc7c7a69830ad38a05",
|
||||
"hex": "0014c772d6f95542e11ef11e8efc7c7a69830ad38a05",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"tb1qcaedd724gts3aug73m78c7nfsv9d8zs9q6h2kd"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"unknown": {
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"witness_utxo": {
|
||||
"amount": 0.00000840,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 d591091b8074a2375ed9985a9c4b18efecfd4165",
|
||||
"hex": "0014d591091b8074a2375ed9985a9c4b18efecfd4165",
|
||||
"type": "witness_v0_keyhash",
|
||||
"address": "tb1q6kgsjxuqwj3rwhkenpdfcjccalk06st9z0k0kh"
|
||||
}
|
||||
},
|
||||
"bip32_derivs": [
|
||||
{
|
||||
"pubkey": "02883bb5463e37d55252d8b3d5c2141b007b37c8a7db6211f75c955acc5ea325eb",
|
||||
"master_fingerprint": "d6043800",
|
||||
"path": "m/0'/1'/12'"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"non_witness_utxo": {
|
||||
"txid": "68e1863492802df9337e6ce4f89f53fa5739837ba459ba52c9a3d200ab0717ee",
|
||||
"hash": "68e1863492802df9337e6ce4f89f53fa5739837ba459ba52c9a3d200ab0717ee",
|
||||
"version": 2,
|
||||
"size": 140,
|
||||
"vsize": 140,
|
||||
"weight": 560,
|
||||
"locktime": 1774654,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "cc49eb0b550fc2dc7e3b9fff459f12650e0903fc7b40a31ede670612f9b266d5",
|
||||
"vout": 1,
|
||||
"scriptSig": {
|
||||
"asm": "0014731714497c53e5f0527a4b18c0273cb798200c5d",
|
||||
"hex": "160014731714497c53e5f0527a4b18c0273cb798200c5d"
|
||||
},
|
||||
"sequence": 4294967294
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.01000000,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_DUP OP_HASH160 7f437379bcc66c40745edc1891ea6b3830e1975d OP_EQUALVERIFY OP_CHECKSIG",
|
||||
"hex": "76a9147f437379bcc66c40745edc1891ea6b3830e1975d88ac",
|
||||
"reqSigs": 1,
|
||||
"type": "pubkeyhash",
|
||||
"addresses": [
|
||||
"ms7ruzvL4atCu77n47dStMb3of6iScS8kZ"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.02961601,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_HASH160 fddfa4317e978a3983fa359d52b2d99495271f68 OP_EQUAL",
|
||||
"hex": "a914fddfa4317e978a3983fa359d52b2d99495271f6887",
|
||||
"reqSigs": 1,
|
||||
"type": "scripthash",
|
||||
"addresses": [
|
||||
"2NGParh82hE2Zif5PVK3AfLpYhfwF5FyRGr"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"bip32_derivs": [
|
||||
{
|
||||
"pubkey": "03efdee34c0009fd175f3b20b5e5a5517fd5d16746f2e635b44617adafeaebc388",
|
||||
"master_fingerprint": "d6043800",
|
||||
"path": "m/0'/0'/18'"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"bip32_derivs": [
|
||||
{
|
||||
"pubkey": "029bb586a52657dd98852cecef78552a4e21d081a7a30e4008ce9b419840d4deac",
|
||||
"master_fingerprint": "d6043800",
|
||||
"path": "m/0'/1'/27'"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
}
|
||||
],
|
||||
"fee": 0.00028800
|
||||
}
|
||||
```
|
||||
Como podemos ver, criamos a PSBT e depois a atualizamos com todas as informações que encontramos localmente.
|
||||
|
||||
A partir daí, precisamos usar o ```walletprocesspsbt``` para finalizar, como de costume:
|
||||
```
|
||||
$ psbt_new_f=$(bitcoin-cli walletprocesspsbt $psbt_new | jq -r '.psbt')
|
||||
```
|
||||
Posteriormente, uma análise mostrará que também está pronto:
|
||||
```
|
||||
$ bitcoin-cli analyzepsbt $psbt_new_f
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"has_utxo": true,
|
||||
"is_final": true,
|
||||
"next": "extractor"
|
||||
},
|
||||
{
|
||||
"has_utxo": true,
|
||||
"is_final": true,
|
||||
"next": "extractor"
|
||||
}
|
||||
],
|
||||
"estimated_vsize": 288,
|
||||
"estimated_feerate": 0.00100000,
|
||||
"fee": 0.00028800,
|
||||
"next": "extractor"
|
||||
}
|
||||
```
|
||||
Agora, realmente gostaríamos de usar o ```walletcreatefundedpsbt``` se estivermos criando um programa com o ```bitcoin-cli```? Provavelmente não. Mas é a mesma análise que fazemos com o ```fundrawtransaction```. Deixamos o Bitcoin Core fazer a análise, o cálculo e as decisões, ou nós mesmos fazemos?
|
||||
|
||||
## Enviando uma PSBT
|
||||
|
||||
#### Função na PSBT: Extrator
|
||||
|
||||
Para finalizar a PSBT, usamos o ```finalizepsbt```, que irá transformar a PSBT novamente em hexadecimal. (Também assumirá a função de Finalizador, caso ainda não tenha acontecido).
|
||||
```
|
||||
$ bitcoin-cli finalizepsbt $psbt_f
|
||||
{
|
||||
"hex": "020000000001021b95a6055174ec64b82ef05b6aefc38f34d0e57197e40281ecd8287b4260dec60100000000ffffffff1cf4c7f70160c9fe22436c70e032033fcb7d22309e3d60e386a82cf1f5ef48870000000000ffffffff018a02000000000000160014c772d6f95542e11ef11e8efc7c7a69830ad38a050247304402200a896f1cfa741ccda5f683bb85d95dbf836c390cdd45a785ece1f54766795821022051c4c956da8d3012da61b5465158888939a1ad5dfb106c1e25a2f92f13879122012102bac3a645fd7a0a537c627ad37f5f695058c58d19ceb0033e913f1a9975e00dee0247304402207eb0d3423b8c62150f83a0756baaa93dc8cd15e07f594b2131c753d6ed16e3b2022051e5a44372fb680bf5c487bb87ef0f65bf040ac5e0d54cdbb4f5bcc1ddaac745012102883bb5463e37d55252d8b3d5c2141b007b37c8a7db6211f75c955acc5ea325eb00000000",
|
||||
"complete": true
|
||||
}
|
||||
```
|
||||
Como de costume, vamos querer salvar e depois enviar.
|
||||
```
|
||||
$ psbt_hex=$(bitcoin-cli finalizepsbt $psbt_f | jq -r '.hex')
|
||||
$ bitcoin-cli -named sendrawtransaction hexstring=$psbt_hex
|
||||
ea73a631b456d2b041ed73bf5767946408c6ff067716929a68ecda2e3e4de6d3
|
||||
```
|
||||
## Revisando o Fluxo de Trabalho
|
||||
|
||||
Ao criar software ```bitcoin-cli```, é mais provável que iremos cumprir as cinco funções principais dos PSBTs usando os comandos ```createpsbt```, ```walletprocesspsbt``` e ```finalizepsbt```. Esta é a aparência desse fluxo:
|
||||
|
||||

|
||||
|
||||
Se escolhermos usar o atalho de ```walletcreatefundedpsbt```, teremos um fluxo mais ou menos assim:
|
||||
|
||||

|
||||
|
||||
Finalmente, se precisarmos de mais controle e optarmos por usar o comando ```utxoupdatepsbt``` (que não está documentado aqui), teremos um fluxo de trabalho assim:
|
||||
|
||||

|
||||
|
||||
## Resumo: Criando uma Transação Bitcoin Parcialmente Assinada
|
||||
|
||||
A criação de uma PSBT envolve um fluxo de trabalho um tanto complexo de criação, atualização, assinatura, finalização e extração da PSBT, após o qual ela se converte novamente em uma transação bruta. Por que teríamos tanto trabalho? Porque desejamos colaborar entre vários usuários ou vários programas. Agora que entendemos esse fluxo de trabalho, a próxima seção irá apresentar alguns exemplos reais de como fazer isso.
|
||||
|
||||
## O Que Vem Depois?
|
||||
|
||||
Vamos continuar "Expandindo Transações no Bitcoin com PSBTs" na seção [§7.2: Usando uma Transação Bitcoin Parcialmente Assinada](07_2_Using_a_Partially_Signed_Bitcoin_Transaction.md).
|
601
pt/07_2_Using_a_Partially_Signed_Bitcoin_Transaction.md
Normal file
601
pt/07_2_Using_a_Partially_Signed_Bitcoin_Transaction.md
Normal file
@ -0,0 +1,601 @@
|
||||
# 7.2: Usando uma Transação Bitcoin Parcialmente Assinada
|
||||
|
||||
> :information_source: **NOTA:** Esta seção foi adicionada recentemente ao curso e é um rascunho inicial que ainda pode estar aguardando revisão.
|
||||
|
||||
Agora que aprendemos o fluxo de trabalho básico para gerar uma PSBT, provavelmente desejamos fazer algo com ela. O que as PSBTs podem fazer que os multisigs (e as transações brutas normais) não podem? Para começar, temos a facilidade de usar um formato padronizado, o que significa que podemos usar nossas transações do ```bitcoin-cli``` e combiná-las com transações geradas por pessoas (ou programas) em outras plataformas. Além disso, podemos fazer algumas coisas que simplesmente não eram fáceis usando outros mecanismos.
|
||||
|
||||
A seguir estão três exemplos do uso das PSBTs para: multisigs, financiamentos e CoinJoins.
|
||||
|
||||
> :aviso: **AVISO DE VERSÃO:** Esta é uma inovação do Bitcoin Core v0.17.0. As versões anteriores do Bitcoin Core não funcionarão com a PSBT enquanto estiver em andamento (embora ainda consigam reconhecer a transação final).
|
||||
|
||||
## Usando uma PSBT para Gastar Fundos MultiSig
|
||||
|
||||
Suponha que tenhamos criado um endereço multisig, assim como fizemos na seção [§6.3](06_3_Sending_an_Automated_Multisig.md).
|
||||
```
|
||||
machine1$ bitcoin-cli -named addmultisigaddress nrequired=2 keys='''["'$pubkey1'","'$pubkey2'"]'''
|
||||
{
|
||||
"address": "tb1qyfxt2qa877p40xdecghwps78my7sjq6kuv88qq2u86al5526xp6qfqjud0",
|
||||
"redeemScript": "5221038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e2103789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd063652ae",
|
||||
"descriptor": "wsh(multi(2,[d6043800/0'/0'/26']038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e,[be686772]03789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd0636))#07zyayfk"
|
||||
}
|
||||
machine1$ bitcoin-cli -named importaddress address="tb1qyfxt2qa877p40xdecghwps78my7sjq6kuv88qq2u86al5526xp6qfqjud0" rescan=false
|
||||
|
||||
machine2$ bitcoin-cli -named addmultisigaddress nrequired=2 keys='''["'$pubkey1'","'$pubkey2'"]'''
|
||||
{
|
||||
"address": "tb1qyfxt2qa877p40xdecghwps78my7sjq6kuv88qq2u86al5526xp6qfqjud0",
|
||||
"redeemScript": "5221038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e2103789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd063652ae",
|
||||
"descriptor": "wsh(multi(2,[d6043800/0'/0'/26']038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e,[be686772]03789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd0636))#07zyayfk"
|
||||
}
|
||||
machine2$ bitcoin-cli -named importaddress address="tb1qyfxt2qa877p40xdecghwps78my7sjq6kuv88qq2u86al5526xp6qfqjud0" rescan=false
|
||||
```
|
||||
E que tenhamos algumas moedas:
|
||||
```
|
||||
$ bitcoin-cli listunspent
|
||||
[
|
||||
{
|
||||
"txid": "53ec62c5c2fe8b16ee2164e9699d16c7b8ac30ec53a696e55f09b79704b539b5",
|
||||
"vout": 0,
|
||||
"address": "tb1qyfxt2qa877p40xdecghwps78my7sjq6kuv88qq2u86al5526xp6qfqjud0",
|
||||
"label": "",
|
||||
"witnessScript": "5221038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e2103789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd063652ae",
|
||||
"scriptPubKey": "0020224cb503a7f7835799b9c22ee0c3c7d93d090356e30e70015c3ebbfa515a3074",
|
||||
"amount": 0.01999800,
|
||||
"confirmations": 2,
|
||||
"spendable": false,
|
||||
"solvable": true,
|
||||
"desc": "wsh(multi(2,[d6043800/0'/0'/26']038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e,[be686772]03789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd0636))#07zyayfk",
|
||||
"safe": true
|
||||
}
|
||||
]
|
||||
```
|
||||
Nós _poderíamos_ gastá-las usando os mecanismos do [Capítulo 6](06_0_Expanding_Bitcoin_Transactions_Multisigs.md), onde assinamos serialmente uma transação, mas ao invés disso, vamos mostrar a vantagem das PSBTs para o uso dos multisigs. Podemos gerar uma única PSBT, permitir que todos a assinem em paralelo e depois podemos combinar todos resultados! Não há mais a necessidade de passar o arquivo hexadecimal em constante expansão de pessoa para pessoa, o que acelera as coisas e reduz as chances de erros.
|
||||
|
||||
Para demonstrar essa metodologia, vamos extrair aquele 0,02 BTC do multisig e dividi-lo entre os dois assinantes, cada um deles gerando um novo endereço com esse propósito:
|
||||
```
|
||||
machine1$ bitcoin-cli getnewaddress
|
||||
tb1qem5l3q5g5h6fsqv352xh4cy07kzq2rd8gphqma
|
||||
machine2$ bitcoin-cli getnewaddress
|
||||
tb1q3krplahg4ncu523m8h2eephjazs2hf6ur8r6zp
|
||||
```
|
||||
A primeira coisa que fazemos é criar uma PSBT em uma máquina. (Não importa qual). Precisamos usar o ```createpsbt``` da seção [§7.1](07_1_Creating_a_Partially_Signed_Bitcoin_Transaction.md) para isso, e não o comando mais simples ```walletcreatefundedpsbt```, porque precisamos do controle extra de selecionar o dinheiro protegido pelo multisig. (Este será o caso para todos os três exemplos nesta seção, o que demonstra por quê geralmente precisamos usar o ```createpsbt``` para coisas mais complexas.
|
||||
```
|
||||
machine1$ utxo_txid=53ec62c5c2fe8b16ee2164e9699d16c7b8ac30ec53a696e55f09b79704b539b5
|
||||
machine1$ utxo_vout=0
|
||||
machine1$ split1=tb1qem5l3q5g5h6fsqv352xh4cy07kzq2rd8gphqma
|
||||
machine1$ split2=tb1q3krplahg4ncu523m8h2eephjazs2hf6ur8r6zp
|
||||
machine1$ psbt=$(bitcoin-cli -named createpsbt inputs='''[ { "txid": "'$utxo_txid'", "vout": '$utxo_vout' } ]''' outputs='''{ "'$split1'": 0.009998,"'$split2'": 0.009998 }''')
|
||||
```
|
||||
Em seguida, precisamos enviar essa $psbt a todo mundo para que assinem:
|
||||
```
|
||||
machine1$ echo $psbt
|
||||
cHNidP8BAHECAAAAAbU5tQSXtwlf5ZamU+wwrLjHFp1p6WQh7haL/sLFYuxTAAAAAAD/////AnhBDwAAAAAAFgAUzun4goil9JgBkaKNeuCP9YQFDad4QQ8AAAAAABYAFI2GH/borPHKKjs91ZyG8uigq6dcAAAAAAAAAAA=
|
||||
```
|
||||
Mas você só precisa enviar uma vez! E você faz isso simultaneamente.
|
||||
|
||||
Aqui está o resultado da primeira máquina, onde geramos a PSBT:
|
||||
```
|
||||
machine1$ psbt_p1=$(bitcoin-cli walletprocesspsbt $psbt | jq -r '.psbt')
|
||||
machine1$ bitcoin-cli decodepsbt $psbt_p1
|
||||
{
|
||||
"tx": {
|
||||
"txid": "1687e89fcb9dd3067f75495b4884dc1d4d1cf05a6c272b783cfe29eb5d22e985",
|
||||
"hash": "1687e89fcb9dd3067f75495b4884dc1d4d1cf05a6c272b783cfe29eb5d22e985",
|
||||
"version": 2,
|
||||
"size": 113,
|
||||
"vsize": 113,
|
||||
"weight": 452,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "25e8a26f60cf485768a1e6953b983675c867b7ab126b02e753c47b7db0c4be5e",
|
||||
"vout": 0,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.00499900,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 cee9f88288a5f4980191a28d7ae08ff584050da7",
|
||||
"hex": "0014cee9f88288a5f4980191a28d7ae08ff584050da7",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"tb1qem5l3q5g5h6fsqv352xh4cy07kzq2rd8gphqma"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00049990,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 8d861ff6e8acf1ca2a3b3dd59c86f2e8a0aba75c",
|
||||
"hex": "00148d861ff6e8acf1ca2a3b3dd59c86f2e8a0aba75c",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"tb1q3krplahg4ncu523m8h2eephjazs2hf6ur8r6zp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"unknown": {
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"witness_utxo": {
|
||||
"amount": 0.01000000,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 2abb5d49ce7e753cbf5a9ffa8cdaf815bf1074f5c0bf495a93df8eb5112f65aa",
|
||||
"hex": "00202abb5d49ce7e753cbf5a9ffa8cdaf815bf1074f5c0bf495a93df8eb5112f65aa",
|
||||
"type": "witness_v0_scripthash",
|
||||
"address": "tb1q92a46jww0e6ne066nlagekhczkl3qa84czl5jk5nm78t2yf0vk4qte328m"
|
||||
}
|
||||
},
|
||||
"partial_signatures": {
|
||||
"03f52980d322acaf084bcef3216f3d84bfb672d1db26ce2861de3ec047bede140d": "304402203abb95d1965e4cea630a8b4890456d56698ff2dd5544cb79303cc28cb011cbb40220701faa927f8a19ca79b09d35c78d8d0a2187872117d9308805f7a896b07733f901"
|
||||
},
|
||||
"witness_script": {
|
||||
"asm": "2 033055ec2da9bbb34c2acb343692bfbecdef8fab8d114f0036eba01baec3888aa0 03f52980d322acaf084bcef3216f3d84bfb672d1db26ce2861de3ec047bede140d 2 OP_CHECKMULTISIG",
|
||||
"hex": "5221033055ec2da9bbb34c2acb343692bfbecdef8fab8d114f0036eba01baec3888aa02103f52980d322acaf084bcef3216f3d84bfb672d1db26ce2861de3ec047bede140d52ae",
|
||||
"type": "multisig"
|
||||
},
|
||||
"bip32_derivs": [
|
||||
{
|
||||
"pubkey": "033055ec2da9bbb34c2acb343692bfbecdef8fab8d114f0036eba01baec3888aa0",
|
||||
"master_fingerprint": "c1fdfe64",
|
||||
"path": "m"
|
||||
{
|
||||
"tx": {
|
||||
"txid": "ee82d3e0d225e0fb919130d68c5052b6e3c362c866acc54d89af975330bb4d16",
|
||||
"hash": "ee82d3e0d225e0fb919130d68c5052b6e3c362c866acc54d89af975330bb4d16",
|
||||
"version": 2,
|
||||
"size": 113,
|
||||
"vsize": 113,
|
||||
"weight": 452,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "53ec62c5c2fe8b16ee2164e9699d16c7b8ac30ec53a696e55f09b79704b539b5",
|
||||
"vout": 0,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.00999800,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 cee9f88288a5f4980191a28d7ae08ff584050da7",
|
||||
"hex": "0014cee9f88288a5f4980191a28d7ae08ff584050da7",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"tb1qem5l3q5g5h6fsqv352xh4cy07kzq2rd8gphqma"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00999800,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 8d861ff6e8acf1ca2a3b3dd59c86f2e8a0aba75c",
|
||||
"hex": "00148d861ff6e8acf1ca2a3b3dd59c86f2e8a0aba75c",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"tb1q3krplahg4ncu523m8h2eephjazs2hf6ur8r6zp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"unknown": {
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"witness_utxo": {
|
||||
"amount": 0.01999800,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 224cb503a7f7835799b9c22ee0c3c7d93d090356e30e70015c3ebbfa515a3074",
|
||||
"hex": "0020224cb503a7f7835799b9c22ee0c3c7d93d090356e30e70015c3ebbfa515a3074",
|
||||
"type": "witness_v0_scripthash",
|
||||
"address": "tb1qyfxt2qa877p40xdecghwps78my7sjq6kuv88qq2u86al5526xp6qfqjud0"
|
||||
}
|
||||
},
|
||||
"partial_signatures": {
|
||||
"038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e": "3044022040aae4f2ba37b1526524195f4a325d97d1317227b3c82aea55c5abd66810a7ec0220416e7c03e70a31232044addba454d6b37b6ace39ab163315d3293e343ae9513301"
|
||||
},
|
||||
"witness_script": {
|
||||
"asm": "2 038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e 03789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd0636 2 OP_CHECKMULTISIG",
|
||||
"hex": "5221038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e2103789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd063652ae",
|
||||
"type": "multisig"
|
||||
},
|
||||
"bip32_derivs": [
|
||||
{
|
||||
"pubkey": "03789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd0636",
|
||||
"master_fingerprint": "be686772",
|
||||
"path": "m"
|
||||
},
|
||||
{
|
||||
"pubkey": "038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e",
|
||||
"master_fingerprint": "d6043800",
|
||||
"path": "m/0'/0'/26'"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"bip32_derivs": [
|
||||
{
|
||||
"pubkey": "02fce26085452d07abc63bd389cb7dba9871e79bbecd08039291226be8232a9000",
|
||||
"master_fingerprint": "d6043800",
|
||||
"path": "m/0'/0'/24'"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
}
|
||||
],
|
||||
"fee": 0.00000200
|
||||
}
|
||||
machine1$ bitcoin-cli analyzepsbt $psbt_p1
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"has_utxo": true,
|
||||
"is_final": false,
|
||||
"next": "signer",
|
||||
"missing": {
|
||||
"signatures": [
|
||||
"be6867729bcc35ed065bb4c937557d371218a8e2"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"estimated_vsize": 168,
|
||||
"estimated_feerate": 0.00001190,
|
||||
"fee": 0.00000200,
|
||||
"next": "signer"
|
||||
}
|
||||
|
||||
```
|
||||
Isso demonstra que as informações do UTXO foram importadas e que há uma _assinatura parcial_, mas que uma assinatura ainda não a deixa completa.
|
||||
|
||||
Aqui temos o mesmo resultado na outra máquina:
|
||||
```
|
||||
machine2$ psbt=cHNidP8BAHECAAAAAbU5tQSXtwlf5ZamU+wwrLjHFp1p6WQh7haL/sLFYuxTAAAAAAD/////AnhBDwAAAAAAFgAUzun4goil9JgBkaKNeuCP9YQFDad4QQ8AAAAAABYAFI2GH/borPHKKjs91ZyG8uigq6dcAAAAAAAAAAA=
|
||||
machine2$ psbt_p2=$(bitcoin-cli walletprocesspsbt $psbt | jq -r '.psbt')
|
||||
machine3$ echo $psbt_p2
|
||||
cHNidP8BAHECAAAAAbU5tQSXtwlf5ZamU+wwrLjHFp1p6WQh7haL/sLFYuxTAAAAAAD/////AnhBDwAAAAAAFgAUzun4goil9JgBkaKNeuCP9YQFDad4QQ8AAAAAABYAFI2GH/borPHKKjs91ZyG8uigq6dcAAAAAAABASu4gx4AAAAAACIAICJMtQOn94NXmbnCLuDDx9k9CQNW4w5wAVw+u/pRWjB0IgIDeJ9UNCNnDhaWZ/9+Hy2iqX3xsJEicuFC1YJFGs69BjZHMEQCIDJ71isvR2We6ym1QByLV5SQ+XEJD0SAP76fe1JU5PZ/AiB3V7ejl2H+9LLS6ubqYr/bSKfRfEqrp2FCMISjrWGZ6QEBBUdSIQONc63yx+oz+dw0t3titZr0M8HenHYzMtp56D4VX5YDDiEDeJ9UNCNnDhaWZ/9+Hy2iqX3xsJEicuFC1YJFGs69BjZSriIGA3ifVDQjZw4Wlmf/fh8toql98bCRInLhQtWCRRrOvQY2ENPtiCUAAACAAAAAgAYAAIAiBgONc63yx+oz+dw0t3titZr0M8HenHYzMtp56D4VX5YDDgRZu4lPAAAiAgNJzEMyT3rZS7QHqb8SvFCv2ee0MKRyVy8bY8tVUDT1KhDT7YglAAAAgAAAAIADAACAAA==
|
||||
```
|
||||
Observe novamente que gerenciamos a assinatura deste multisig gerando uma PSBT totalmente sem assinatura com o UTXO correto, permitindo que cada um dos usuários processe essa PSBT por conta própria, adicionando entradas e assinaturas. Como resultado, temos duas PSBTs, cada uma contendo apenas uma assinatura. Isso não funcionaria no cenário multisig clássico, porque todas as assinaturas devem ser serializadas. Porém, podemos assinar em paralelo neste momento, e em seguida, usar a função Combinador para juntá-las.
|
||||
|
||||
Vamos novamente a qualquer uma das máquinas e vamos nos certificar de que temos as duas PSBTs nas variáveis e, em seguida, vamos combiná-las:
|
||||
```
|
||||
machine1$ psbt_p2="cHNidP8BAHECAAAAAbU5tQSXtwlf5ZamU+wwrLjHFp1p6WQh7haL/sLFYuxTAAAAAAD/////AnhBDwAAAAAAFgAUzun4goil9JgBkaKNeuCP9YQFDad4QQ8AAAAAABYAFI2GH/borPHKKjs91ZyG8uigq6dcAAAAAAABAIcCAAAAAtu5pTheUzdsTaMCEPj3XKboMAyYzABmIIeOWMhbhTYlAAAAAAD//////uSTLbibcqSd/Z9ieSBWJ2psv+9qvoGrzWEa60rCx9cAAAAAAP////8BuIMeAAAAAAAiACAiTLUDp/eDV5m5wi7gw8fZPQkDVuMOcAFcPrv6UVowdAAAAAAAACICA0nMQzJPetlLtAepvxK8UK/Z57QwpHJXLxtjy1VQNPUqENPtiCUAAACAAAAAgAMAAIAA"
|
||||
machine2$ psbt_c=$(bitcoin-cli combinepsbt '''["'$psbt_p1'", "'$psbt_p2'"]''')
|
||||
$ bitcoin-cli decodepsbt $psbt_c
|
||||
{
|
||||
"tx": {
|
||||
"txid": "ee82d3e0d225e0fb919130d68c5052b6e3c362c866acc54d89af975330bb4d16",
|
||||
"hash": "ee82d3e0d225e0fb919130d68c5052b6e3c362c866acc54d89af975330bb4d16",
|
||||
"version": 2,
|
||||
"size": 113,
|
||||
"vsize": 113,
|
||||
"weight": 452,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "53ec62c5c2fe8b16ee2164e9699d16c7b8ac30ec53a696e55f09b79704b539b5",
|
||||
"vout": 0,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.00999800,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 cee9f88288a5f4980191a28d7ae08ff584050da7",
|
||||
"hex": "0014cee9f88288a5f4980191a28d7ae08ff584050da7",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"tb1qem5l3q5g5h6fsqv352xh4cy07kzq2rd8gphqma"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00999800,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 8d861ff6e8acf1ca2a3b3dd59c86f2e8a0aba75c",
|
||||
"hex": "00148d861ff6e8acf1ca2a3b3dd59c86f2e8a0aba75c",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"tb1q3krplahg4ncu523m8h2eephjazs2hf6ur8r6zp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"unknown": {
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"witness_utxo": {
|
||||
"amount": 0.01999800,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 224cb503a7f7835799b9c22ee0c3c7d93d090356e30e70015c3ebbfa515a3074",
|
||||
"hex": "0020224cb503a7f7835799b9c22ee0c3c7d93d090356e30e70015c3ebbfa515a3074",
|
||||
"type": "witness_v0_scripthash",
|
||||
"address": "tb1qyfxt2qa877p40xdecghwps78my7sjq6kuv88qq2u86al5526xp6qfqjud0"
|
||||
}
|
||||
},
|
||||
"partial_signatures": {
|
||||
"038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e": "3044022040aae4f2ba37b1526524195f4a325d97d1317227b3c82aea55c5abd66810a7ec0220416e7c03e70a31232044addba454d6b37b6ace39ab163315d3293e343ae9513301",
|
||||
"03789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd0636": "30440220327bd62b2f47659eeb29b5401c8b579490f971090f44803fbe9f7b5254e4f67f02207757b7a39761fef4b2d2eae6ea62bfdb48a7d17c4aaba761423084a3ad6199e901"
|
||||
},
|
||||
"witness_script": {
|
||||
"asm": "2 038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e 03789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd0636 2 OP_CHECKMULTISIG",
|
||||
"hex": "5221038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e2103789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd063652ae",
|
||||
"type": "multisig"
|
||||
},
|
||||
"bip32_derivs": [
|
||||
{
|
||||
"pubkey": "03789f543423670e169667ff7e1f2da2a97df1b0912272e142d582451acebd0636",
|
||||
"master_fingerprint": "be686772",
|
||||
"path": "m"
|
||||
},
|
||||
{
|
||||
"pubkey": "038d73adf2c7ea33f9dc34b77b62b59af433c1de9c763332da79e83e155f96030e",
|
||||
"master_fingerprint": "d6043800",
|
||||
"path": "m/0'/0'/26'"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"bip32_derivs": [
|
||||
{
|
||||
"pubkey": "02fce26085452d07abc63bd389cb7dba9871e79bbecd08039291226be8232a9000",
|
||||
"master_fingerprint": "d6043800",
|
||||
"path": "m/0'/0'/24'"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bip32_derivs": [
|
||||
{
|
||||
"pubkey": "0349cc43324f7ad94bb407a9bf12bc50afd9e7b430a472572f1b63cb555034f52a",
|
||||
"master_fingerprint": "d3ed8825",
|
||||
"path": "m/0'/0'/3'"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"fee": 0.00000200
|
||||
}
|
||||
$ bitcoin-cli analyzepsbt $psbt_c
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"has_utxo": true,
|
||||
"is_final": false,
|
||||
"next": "finalizer"
|
||||
}
|
||||
],
|
||||
"estimated_vsize": 168,
|
||||
"estimated_feerate": 0.00001190,
|
||||
"fee": 0.00000200,
|
||||
"next": "finalizer"
|
||||
}
|
||||
```
|
||||
Funcionou! Acabamos de finalizar, enviar e pronto:
|
||||
```
|
||||
machine2$ psbt_c_hex=$(bitcoin-cli finalizepsbt $psbt_c | jq -r '.hex')
|
||||
standup@btctest2:~$ bitcoin-cli -named sendrawtransaction hexstring=$psbt_c_hex
|
||||
ee82d3e0d225e0fb919130d68c5052b6e3c362c866acc54d89af975330bb4d16
|
||||
```
|
||||
Obviamente, não houve uma grande melhoria no uso deste método em relação à assinatura em série de uma transação para um multisig 2-de-2 quando todos estavam usando o ```bitcoin-cli```: poderíamos ter passado uma transação bruta com assinaturas parciais de um usuário para o outro tão facilmente quanto enviar a PSBT. Mas, este foi o caso mais simples. À medida que nos aprofundamos em multisigs mais complexos, essa metodologia se torna cada vez melhor, mas já podemos destacar alguns pontos importantes:
|
||||
|
||||
Em primeiro lugar, é independente de plataforma. Enquanto todos estiverem usando um serviço compatível com o Bitcoin Core 0.17, todos poderão assinar essa transação, o que não é verdade quando os multisigs clássicos estão sendo repassados entre diferentes plataformas.
|
||||
|
||||
Em segundo lugar, é muito mais escalável. Considere um multisig 3-de-5. De acordo com a metodologia antiga, isso teria que ser transmitido de pessoa para pessoa, aumentando enormemente os problemas se alguém o corrompesse. Aqui, outros usuários precisam apenas enviar as PSBTs de volta ao Criador e, assim que tiver a quantidade suficiente, poderá gerar a transação final.
|
||||
|
||||
## Usando uma PSBT Para Financiamentos
|
||||
|
||||
Os Multisigs como o usado no exemplo anterior são frequentemente usados para receber pagamentos por trabalho colaborativo, seja royalties de um livro ou pagamentos feitos a uma empresa. Nessa situação, o exemplo acima funciona muito bem: os dois participantes recebem seu dinheiro, que depois é dividido. Mas e quanto ao caso inverso, em que dois (ou mais) participantes desejam criar uma _joint venture_ e precisam dividir o dinheiro?
|
||||
|
||||
A resposta tradicional é criar um multisig e depois fazer com que os participantes enviem individualmente os fundos para lá. O problema é que o primeiro pagador depende da boa fé do segundo, e isso não é a base do Bitcoin, que diz que _não devemos confiar_. Felizmente, com o advento das PSBTs, agora podemos fazer pagamentos sem confiança para podemos enviar fundos.
|
||||
|
||||
> :book: ***O que significa a ideia de não podermos confiar?*** Quando dizemos que não devemos confiar, significa que nenhum participante precisa confiar em nenhum outro participante. Ao invés disso, eles esperam que os protocolos de software garantam que tudo seja executado de maneira justa e esperada. O Bitcoin é um protocolo que não depende de confiança porque não precisamos que ninguém mais aja de boa fé. O sistema faz o gerenciamento. Da mesma forma, as PSBTs permitem a criação de transações que agrupam ou dividem fundos sem a necessidade de nenhuma parte confiar na outra.
|
||||
|
||||
O exemplo a seguir mostra dois usuários que possuem 0,010 BTC e que desejam agrupar no endereço multisig `tb1qyfxt2qa877p40xdecghwps78my7sjq6kuv88qq2u86al5526xp6qfqjud0` criado acima.
|
||||
```
|
||||
machine1$ bitcoin-cli listunspent
|
||||
[
|
||||
{
|
||||
"txid": "2536855bc8588e87206600cc980c30e8a65cf7f81002a34d6c37535e38a5b9db",
|
||||
"vout": 0,
|
||||
"address": "tb1qfg5y4fx979xkv4ezatc5eevufc8vh45553n4ut",
|
||||
"label": "",
|
||||
"scriptPubKey": "00144a284aa4c5f14d665722eaf14ce59c4e0ecbd694",
|
||||
"amount": 0.01000000,
|
||||
"confirmations": 2,
|
||||
"spendable": true,
|
||||
"solvable": true,
|
||||
"desc": "wpkh([d6043800/0'/0'/25']02bea222cf9ea1f49b392103058cc7c8741d76a553fe627c1c43fc3ef4404c9d54)#4hnkg9ml",
|
||||
"safe": true
|
||||
}
|
||||
]
|
||||
machine2$ bitcoin-cli listunspent
|
||||
[
|
||||
{
|
||||
"txid": "d7c7c24aeb1a61cdab81be6aefbf6c6a27562079629ffd9da4729bb82d93e4fe",
|
||||
"vout": 0,
|
||||
"address": "tb1qfqyyw6xrghm5kcrpkus3kl2l6dz4tpwrvn5ujs",
|
||||
"label": "",
|
||||
"scriptPubKey": "001448084768c345f74b6061b7211b7d5fd3455585c3",
|
||||
"amount": 0.01000000,
|
||||
"confirmations": 5363,
|
||||
"spendable": true,
|
||||
"solvable": true,
|
||||
"desc": "wpkh([d3ed8825/0'/0'/0']03ff6b94c119582a63dbae4fb530efab0ed5635f7c3b2cf171264ca0af3ecef33a)#gtmd2e2k",
|
||||
"safe": true
|
||||
}
|
||||
]
|
||||
```
|
||||
Eles configuram variáveis para usarem essas transações:
|
||||
```
|
||||
machine1$ utxo_txid_1=2536855bc8588e87206600cc980c30e8a65cf7f81002a34d6c37535e38a5b9db
|
||||
machine1$ utxo_vout_1=0
|
||||
machine1$ utxo_txid_2=d7c7c24aeb1a61cdab81be6aefbf6c6a27562079629ffd9da4729bb82d93e4fe
|
||||
machine1$ utxo_vout_2=0
|
||||
machine1$ multisig=tb1qyfxt2qa877p40xdecghwps78my7sjq6kuv88qq2u86al5526xp6qfqjud0
|
||||
|
||||
```
|
||||
E criam uma PSBT:
|
||||
```
|
||||
machine1$ psbt=$(bitcoin-cli -named createpsbt inputs='''[ { "txid": "'$utxo_txid_1'", "vout": '$utxo_vout_1' }, { "txid": "'$utxo_txid_2'", "vout": '$utxo_vout_2' } ]''' outputs='''{ "'$multisig'": 0.019998 }''')
|
||||
```
|
||||
O resultado fica mais ou menos assim:
|
||||
```
|
||||
machine1$ bitcoin-cli decodepsbt $psbt
|
||||
{
|
||||
"tx": {
|
||||
"txid": "53ec62c5c2fe8b16ee2164e9699d16c7b8ac30ec53a696e55f09b79704b539b5",
|
||||
"hash": "53ec62c5c2fe8b16ee2164e9699d16c7b8ac30ec53a696e55f09b79704b539b5",
|
||||
"version": 2,
|
||||
"size": 135,
|
||||
"vsize": 135,
|
||||
"weight": 540,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "2536855bc8588e87206600cc980c30e8a65cf7f81002a34d6c37535e38a5b9db",
|
||||
"vout": 0,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967295
|
||||
},
|
||||
{
|
||||
"txid": "d7c7c24aeb1a61cdab81be6aefbf6c6a27562079629ffd9da4729bb82d93e4fe",
|
||||
"vout": 0,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.01999800,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 224cb503a7f7835799b9c22ee0c3c7d93d090356e30e70015c3ebbfa515a3074",
|
||||
"hex": "0020224cb503a7f7835799b9c22ee0c3c7d93d090356e30e70015c3ebbfa515a3074",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_scripthash",
|
||||
"addresses": [
|
||||
"tb1qyfxt2qa877p40xdecghwps78my7sjq6kuv88qq2u86al5526xp6qfqjud0"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"unknown": {
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
},
|
||||
{
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
Não importa se as transações sejam de propriedade de duas pessoas diferentes ou que as informações completas apareçam em duas máquinas diferentes. Esta PSBT de financiamento funcionará exatamente da mesma forma que a PSBT multisig. Uma vez que todos os controladores tenham assinado, a transação pode ser finalizada.
|
||||
|
||||
Aqui está o processo, desta vez passando a PSBT parcialmente assinada de um usuário para outro, ao invés de ter que combinar as coisas no final.
|
||||
```
|
||||
machine1$ bitcoin-cli walletprocesspsbt $psbt
|
||||
{
|
||||
"psbt": "cHNidP8BAIcCAAAAAtu5pTheUzdsTaMCEPj3XKboMAyYzABmIIeOWMhbhTYlAAAAAAD//////uSTLbibcqSd/Z9ieSBWJ2psv+9qvoGrzWEa60rCx9cAAAAAAP////8BuIMeAAAAAAAiACAiTLUDp/eDV5m5wi7gw8fZPQkDVuMOcAFcPrv6UVowdAAAAAAAAQEfQEIPAAAAAAAWABRKKEqkxfFNZlci6vFM5ZxODsvWlAEIawJHMEQCIGAiKIAWRXiw68o3pw61/cVNP7n2oH73S654XXgQ4kjHAiBtTBqmaF1iIzYGXrG4DadH8y6mTuCRVFDiPl+TLQDBJwEhAr6iIs+eofSbOSEDBYzHyHQddqVT/mJ8HEP8PvRATJ1UAAABAUdSIQONc63yx+oz+dw0t3titZr0M8HenHYzMtp56D4VX5YDDiEDeJ9UNCNnDhaWZ/9+Hy2iqX3xsJEicuFC1YJFGs69BjZSriICA3ifVDQjZw4Wlmf/fh8toql98bCRInLhQtWCRRrOvQY2BL5oZ3IiAgONc63yx+oz+dw0t3titZr0M8HenHYzMtp56D4VX5YDDhDWBDgAAAAAgAAAAIAaAACAAA==",
|
||||
"complete": false
|
||||
}
|
||||
|
||||
machine2$ psbt_p="cHNidP8BAIcCAAAAAtu5pTheUzdsTaMCEPj3XKboMAyYzABmIIeOWMhbhTYlAAAAAAD//////uSTLbibcqSd/Z9ieSBWJ2psv+9qvoGrzWEa60rCx9cAAAAAAP////8BuIMeAAAAAAAiACAiTLUDp/eDV5m5wi7gw8fZPQkDVuMOcAFcPrv6UVowdAAAAAAAAQEfQEIPAAAAAAAWABRKKEqkxfFNZlci6vFM5ZxODsvWlAEIawJHMEQCIGAiKIAWRXiw68o3pw61/cVNP7n2oH73S654XXgQ4kjHAiBtTBqmaF1iIzYGXrG4DadH8y6mTuCRVFDiPl+TLQDBJwEhAr6iIs+eofSbOSEDBYzHyHQddqVT/mJ8HEP8PvRATJ1UAAABAUdSIQONc63yx+oz+dw0t3titZr0M8HenHYzMtp56D4VX5YDDiEDeJ9UNCNnDhaWZ/9+Hy2iqX3xsJEicuFC1YJFGs69BjZSriICA3ifVDQjZw4Wlmf/fh8toql98bCRInLhQtWCRRrOvQY2BL5oZ3IiAgONc63yx+oz+dw0t3titZr0M8HenHYzMtp56D4VX5YDDhDWBDgAAAAAgAAAAIAaAACAAA=="
|
||||
machine2$ psbt_f=$(bitcoin-cli walletprocesspsbt $psbt_p | jq -r '.psbt')
|
||||
machine2$ bitcoin-cli analyzepsbt $psbt_f
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"has_utxo": true,
|
||||
"is_final": true,
|
||||
"next": "extractor"
|
||||
},
|
||||
{
|
||||
"has_utxo": true,
|
||||
"is_final": true,
|
||||
"next": "extractor"
|
||||
}
|
||||
],
|
||||
"estimated_vsize": 189,
|
||||
"estimated_feerate": 0.00001058,
|
||||
"fee": 0.00000200,
|
||||
"next": "extractor"
|
||||
}
|
||||
machine2$ psbt_hex=$(bitcoin-cli finalizepsbt $psbt_f | jq -r '.hex')
|
||||
machine2$ bitcoin-cli -named sendrawtransaction hexstring=$psbt_hex
|
||||
53ec62c5c2fe8b16ee2164e9699d16c7b8ac30ec53a696e55f09b79704b539b5
|
||||
```
|
||||
Usamos uma PSBT para reunir o dinheiro usando um multisig sem precisar de confiança!
|
||||
|
||||
## Usando uma PSBT para CoinJoin
|
||||
|
||||
O CoinJoin é outra aplicação no Bitcoin que não requer confiança. Aqui, temos uma variedade de entes que não se conhecem juntando dinheiro e recebendo de volta.
|
||||
|
||||
A metodologia para gerenciá-lo com PSBTs é exatamente a mesma que vimos nos exemplos acima, como o seguinte pseudocódigo demonstra:
|
||||
```
|
||||
$ psbt=$(bitcoin-cli -named createpsbt inputs='''[ { "txid": "'$utxo_txid_1'", "vout": '$utxo_vout_1' }, { "txid": "'$utxo_txid_2'", "vout": '$utxo_vout_2' }, { "txid": "'$utxo_txid_3'", "vout": '$utxo_vout_3' } ]''' outputs='''{ "'$split1'": 1.7,"'$split2'": 0.93,"'$split3'": 1.4 }''')
|
||||
```
|
||||
Cada usuário coloca o próprio UTXO e cada um recebe uma saída correspondente.
|
||||
|
||||
A melhor maneira de gerenciar um CoinJoin é enviar a PSBT básica para todas as partes (que podem ser inúmeras) e, em seguida, fazer com que cada uma assine a PSBT e envie de volta para uma única parte que irá combinar, finalizar e enviar.
|
||||
|
||||
## Resumo: Usando uma Transação Bitcoin Parcialmente Assinada
|
||||
|
||||
Agora vimos o processo PSBT que aprendemos na seção [§7.1](07_1_Creating_a_Partially_Signed_Bitcoin_Transaction.md) em uso em três exemplos da vida real: criando um multisig, um financiamento e fazendo CoinJoin. Tudo isso era teoricamente possível no Bitcoin com várias pessoas assinando transações cuidadosamente construídas, mas as PSBTs tornam isso padronizado e simples.
|
||||
|
||||
> :fire: ***Qual é o poder de uma PSBT?*** Uma PSBT permite a criação de transações sem necessidade de confiança entre várias partes e várias máquinas. Se mais de uma parte precisar financiar uma transação, se mais de uma parte precisar assinar uma transação, ou se uma transação precisar ser criada em uma máquina e assinada em outra, uma PSBT torna isso simples, sem depender do mecanismos de assinatura parcial não padronizados que existiam antes da PSBT.
|
||||
|
||||
Esse último ponto, sobre criar uma transação em uma máquina e assinar em outra, é um elemento das PSBTs ao qual ainda não chegamos. Ele está no centro das carteiras de hardware, onde geralmente desejamos criar uma transação em um full node e, em seguida, passá-la para uma hardware wallet quando uma assinatura for necessária. Esse é o tópico da nossa última seção (e do nosso quarto exemplo da vida real) deste capítulo sobre PSBTs.
|
||||
|
||||
## O Que Vem Depois?
|
||||
|
||||
Vamos continuar "Expandindo Transações no Bitcoin com PSBTs" na seção [§7.3: Integrando com Hardware Wallets](07_3_Integrating_with_Hardware_Wallets.md).
|
469
pt/07_3_Integrating_with_Hardware_Wallets.md
Normal file
469
pt/07_3_Integrating_with_Hardware_Wallets.md
Normal file
@ -0,0 +1,469 @@
|
||||
# 7.3: Integrando com Hardware Wallets
|
||||
|
||||
> :information_source: **NOTA:** Esta seção foi adicionada recentemente ao curso e é um rascunho inicial que ainda pode estar aguardando revisão. Leitor de advertência.
|
||||
|
||||
Uma das principais vantagens das PSBTs é a capacidade de transferir transações para hardware wallets. Esta será uma ótima ferramenta de desenvolvimento para nós continuarmos a programar com Bitcoin. No entanto, não podemos testá-la agora se estivermos usando uma das configurações que sugerimos para este curso, uma VM no Linode de acordo com a seção [§2.1](https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line/blob/master/02_1_Setting_Up_a_Bitcoin-Core_VPS_with_StackScript.md) ou uma opção mais expansiva ainda como um AWS de acordo com a seção [§2.2](https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line/blob/master/02_2_Setting_Up_Bitcoin_Core_Other.md), isso porque obviamente não teremos como conectar uma hardware wallet à nossa máquina virtual remota.
|
||||
|
||||
> :book: ***O que é uma hardware wallet?*** Uma hardware wallet é um dispositivo eletrônico que melhora a segurança da criptomoeda mantendo todas as chaves privadas no dispositivo, ao invés de colocá-las em um computador conectado diretamente a internet. As hardware wallets têm protocolos específicos para fornecer interações online, geralmente gerenciadas por um programa que se comunica com o dispositivo por meio de uma porta USB. Neste capítulo, gerenciaremos uma hardware wallet com o ```bitcoin-cli``` e o programa ```hwy.py```.
|
||||
|
||||
Existem três opções de como passar por este capítulo: (1) ler sem testar o código; (2) instalar o Bitcoin em uma máquina local para testar todos esses comandos ou; (3) pular direto para o [Capítulo 8: Expandindo Transações no Bitcoin de Outras Maneiras](08_0_Expanding_Bitcoin_Transactions_Other.md). Sugerimos a primeira opção, mas se quisermos colocar a mão na massa, iremos dar o suporte necessário para a segunda opção falando sobre o uso de um Macintosh (uma plataforma hardware que o [Bitcoin Standup](https://github.com/BlockchainCommons/Bitcoin-Standup) dá suporte) para teste.
|
||||
|
||||
> :warning: **AVISO DE VERSÃO:** As PSBTs são uma inovação do Bitcoin Core v0.17.0. As versões anteriores do Bitcoin Core não funcionarão com o PSBT enquanto ele estiver em andamento (embora ainda consigam reconhecer a transação final). A interface HWI apareceu no Bitcoin Core v 0.18.0, mas, desde que estejamos usando nossa configuração sugerida com o Bitcoin Standup, ela deve funcionar.
|
||||
|
||||
A metodologia descrita neste capítulo para integração com uma hardware wallet depende do [Bitcoin Hardware Wallet Interface](https://github.com/bitcoin-core/HWI) lançada através do Bitcoin Core e que se baseia nas instruções de [instalação](https://github.com/bitcoin-core/HWI/blob/master/README.md) e [uso](https://hwi.readthedocs.io) contidas nele.
|
||||
|
||||
> :warning: **AVISO DE NOVIDADE:** A interface HWI é muito nova e precisa de alguns ajustes ainda, mesmo na v0.20.0 do Bitcoin Core. Pode ser difícil instalá-la corretamente e pode conter erros não intuitivos. O que segue é uma descrição de uma configuração que funciona, mas foram necessárias várias tentativas para ter sucesso e sua configuração pode variar.
|
||||
|
||||
## Instalando o Bitcoin Core em uma Máquina Local
|
||||
|
||||
_Se pretendemos apenas ler esta seção e não testar os comandos, podemos pular esta subseção, que basicamente explicará como criar uma instalação Bitcoin Core em uma máquina local, como uma máquina Mac ou Linux._
|
||||
|
||||
Existem versões alternativas do script Bitcoin Standup que usamos para criar nossa VM que será instalada em um MacOS ou em uma máquina Linux que não seja o Linode.
|
||||
|
||||
Se tivermos MacOS, podemos instalar o [Bitcoin Standup MacOS](https://github.com/BlockchainCommons/Bitcoin-Standup-MacOS/blob/master/README.md).
|
||||
|
||||
Se tivermos uma máquina Linux local, podemos instalar o [Bitcoin Standup Linux Scripts](https://github.com/BlockchainCommons/Bitcoin-Standup-MacOS/blob/master/README.md).
|
||||
|
||||
Depois de colocar o Bitcoin Standup em execução em nossa máquina local, vamos desejar sincronizar o blockchain "Testnet", assumindo que já estamos seguindo o método padrão deste curso.
|
||||
|
||||
Estaremos usando um Macintosh e um Testnet para os exemplos desta seção.
|
||||
|
||||
### Criando um Alias para o Bitcoin-CLI
|
||||
|
||||
Crie um alias que execute o ```bitcoin-cli``` no diretório correto com quaisquer flags apropriadas.
|
||||
|
||||
Aqui está um exemplo de um alias de um Mac:
|
||||
```
|
||||
$ alias bitcoin-cli="~/StandUp/BitcoinCore/bitcoin-0.20.0/bin/bitcoin-cli -testnet"
|
||||
```
|
||||
Podemos notar que ele nos dá não apenas o caminho completo, mas também garante que permaneçamos na Testnet.
|
||||
|
||||
## Instalando o HWI em uma Máquina Local
|
||||
|
||||
_As instruções a seguir novamente presumem um Mac e podemos pular novamente esta subseção se estivermos apenas lendo o capítulo._
|
||||
|
||||
O HWI é um programa do Bitcoin Core disponível em python que podemos usar para interagir com as hardware wallets.
|
||||
|
||||
### Instalando Python
|
||||
|
||||
Como o HWI é escrito em ```python```, precisaremos instalá-lo, bem como alguns programas auxiliares.
|
||||
|
||||
Se ainda não temos as ferramentas de linha de comando ```xcode```, iremos precisar delas:
|
||||
```
|
||||
$ xcode-select --install
|
||||
```
|
||||
Se ainda não temos o gerenciador de pacotes Homebrew, precisamos instalá-lo também. As instruções atuais estão disponíveis no [site do Homebrew](https://brew.sh/). No momento em que este livro foi escrito, basta utilizarmos o comando abaixo:
|
||||
```
|
||||
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
|
||||
```
|
||||
|
||||
Para uma instalação pela primeira vez, também devemos nos certificar de que nosso diretório ```/usr/local/Frameworks``` foi criado corretamente:
|
||||
```
|
||||
$ sudo mkdir /usr/local/Frameworks
|
||||
$ sudo chown $(whoami):admin /usr/local/Frameworks
|
||||
```
|
||||
Se já fizemos tudo isso, podemos finalmente instalar o Python:
|
||||
```
|
||||
$ brew install python
|
||||
$ brew install libusb
|
||||
```
|
||||
|
||||
### Instalando o HWI
|
||||
|
||||
Agora estamos prontos para instalar o HWI, o que requer a clonagem de um repositório GitHub e a execução de um script de instalação.
|
||||
|
||||
Se ainda não temos o ```git``` instalado em nosso Mac, podemos fazer isso apenas executando o comando: ```git --version```.
|
||||
|
||||
Podemos então clonar o repositório HWI:
|
||||
```
|
||||
$ cd ~/StandUp
|
||||
$ git clone https://github.com/bitcoin-core/HWI.git
|
||||
```
|
||||
Depois, precisaremos instalar o pacote e as dependências:
|
||||
```
|
||||
$ cd HWI
|
||||
HWI$ python3 setup.py install
|
||||
```
|
||||
### Criando um Alias para o HWI
|
||||
|
||||
Iremos querer criar um alias aqui também, variando de acordo com o local de instalação real:
|
||||
```
|
||||
$ alias hwi="~/Standup/HWI/hwi.py --testnet"
|
||||
```
|
||||
Novamente, incluímos uma referência a testnet no alias.
|
||||
|
||||
## Preparando nossa Ledger
|
||||
|
||||
_Tivemos que escolher uma plataforma de hardware wallet para a demonstração de HWI. Nossa escolha foi a Ledger, que há muito tempo é onde fazemos os testes nestes casos. Podemos consultar as [informações de suporte do dispositivo do HWI](https://github.com/bitcoin-core/HWI/blob/master/README.md#device-support) para obter uma lista de outros dispositivos compatíveis. Se usarmos um dispositivo diferente de uma Ledger, precisaremos avaliar as nossas soluções para prepará-la para uso na Testnet, mas caso contrário, devemos ser capazes de continuar com o curso conforme escrito aqui._
|
||||
|
||||
Se estiver trabalhando com Bitcoins usando a Ledger, provavelmente não precisaremos fazer nada. (Mas não sugerimos isso neste curso).
|
||||
|
||||
Para trabalhar com moedas da Testnet, conforme sugerido aqui, precisaremos fazer algumas atualizações:
|
||||
|
||||
1. Vamos em Configurações em nosso aplicativo Ledger Live (clicando na engrenagem), depois na guia "Recursos Experimentais" e vamos ativar o "Modo de Desenvolvedor";
|
||||
2. Vamos no "Gerenciar" para instalar o "Bitcoin Test". A versão atual requer que tenhamos o "Bitcoin" e o "Ethereum" instalados primeiro;
|
||||
3. Vamos ao "Gerenciar", para instalar o nosso novo "Bitcoin Test" e "Adicionar uma conta".
|
||||
|
||||
## Acoplando a Ledger
|
||||
|
||||
Para que uma Ledger esteja acessível, devemos fazer o login com o nosso PIN e, em seguida, acessar o aplicativo que desejamos usar, que no caso é o aplicativo "Bitcoin Test". Pode ser necessário repetir isso algumas vezes quando nossa Ledger ficar em stand-by.
|
||||
|
||||
Depois, podemos solicitar que o HWI acesse a Ledger com o comando `enumerate`:
|
||||
```
|
||||
$ hwi enumerate
|
||||
[{"type": "ledger", "model": "ledger_nano_s", "path": "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/XHC1@14/XHC1@14000000/HS05@14100000/Nano S@14100000/Nano S@0/IOUSBHostHIDDevice@14100000,0", "fingerprint": "9a1d520b", "needs_pin_sent": false, "needs_passphrase_sent": false}]
|
||||
```
|
||||
Se recebermos algumas informações no nosso dispositivo, está tudo certo! Como podemos observar, ele verifica o tipo da hardware wallet, fornecendo outras informações de identificação e também informa como se comunicar com o dispositivo. A ```fingerprint``` (```9a1d520b```) é o que devemos prestar atenção especial, porque todas as interações com nossa hardware wallet irão exigir essa informação.
|
||||
|
||||
Se, ao invés disso, obtivermos como resultado ```[ ]```, então ou (1) não deixamos nosso dispositivo Ledger habilitado com o PIN correto e não escolhemos o aplicativo correto, ou (2) há algo de errado com nossa configuração Python, provavelmente uma dependência esteja faltando: devemos considerar usar o comando ```uninstall``` e tentar do zero novamente.
|
||||
|
||||
## Importando Endereços
|
||||
|
||||
A interação com uma hardware wallet geralmente ocorre em duas partes: observando os saldos e gastando-os.
|
||||
|
||||
Podemos monitorar os fundos importando os endereços da nossa hardware wallet para o full node, usando o HWI e o ```bitcoin-cli```.
|
||||
|
||||
### Criando uma Carteira
|
||||
|
||||
Para usar nossa hardware wallet com o ```bitcoin-cli```, precisaremos criar uma carteira com nome específico no Bitcoin Core, usando o RPC ```createwallet```, que é um comando que não discutimos anteriormente.
|
||||
```
|
||||
$ bitcoin-cli --named createwallet wallet_name="ledger" disable_private_keys="true"
|
||||
{
|
||||
"name": "ledger",
|
||||
"warning": ""
|
||||
}
|
||||
```
|
||||
Neste caso, estamos criando uma carteira chamada `ledger` sem chaves privadas (já que estas estarão no dispositivo Ledger).
|
||||
|
||||
> :book: ***Por que nomear as carteiras?*** Até agora, este curso usou uma carteira padrão ("") no Bitcoin Core. Isso é bom para muitos propósitos, mas é inadequado se tivermos uma situação mais complexa, como quando estamos usando as chaves de uma hardware wallet. Aqui, queremos ser capazes de diferenciar as chaves locais (que são mantidas na carteira "") e as chaves da Ledger (que são mantidas na carteira "ledger").
|
||||
|
||||
Agora podemos observar que a nova carteira está em nossa lista de carteiras:
|
||||
```
|
||||
$ bitcoin-cli listwallets
|
||||
[
|
||||
"",
|
||||
"ledger"
|
||||
]
|
||||
```
|
||||
Como criamos uma segunda carteira, alguns comandos exigirão um sinalizador ```-rpcwallet=```, para especificar qual delas estamos utilizando.
|
||||
|
||||
### Importando as Chaves
|
||||
|
||||
Agora temos que importar uma lista de observação de endereços da hardware wallet. Isso é feito com o comando ```getkeypool``` do HWI:
|
||||
```
|
||||
$ hwi -f 9a1d520b getkeypool --wpkh 0 1000
|
||||
[{"desc": "wpkh([9a1d520b/84h/1h/0h]tpubDD7KTtoGzK9GuWUQcr1uTJazsAkqoXhdrwGXWVix6nPpNZmSbagZWD4QSaMsyK8YohAirGDPrWdRiEpKzTFB7DrTrqfzHCn7yi5EsqeR93S/0/*)#qttxy592", "range": [0, 1000], "timestamp": "now", "internal": false, "keypool": true, "active": true, "watchonly": true}, {"desc": "wpkh([9a1d520b/84h/1h/0h]tpubDD7KTtoGzK9GuWUQcr1uTJazsAkqoXhdrwGXWVix6nPpNZmSbagZWD4QSaMsyK8YohAirGDPrWdRiEpKzTFB7DrTrqfzHCn7yi5EsqeR93S/1/*)#3lw8ep4j", "range": [0, 1000], "timestamp": "now", "internal": true, "keypool": true, "active": true, "watchonly": true}]
|
||||
```
|
||||
Endereçamos o HWI com a ```fingerprint``` e solicitamos os primeiros 1000 endereços WPKH (Segwit nativo). Em troca, recebemos dois descritores para o conjunto de chaves: um para receber endereços e outro para alterar endereços.
|
||||
|
||||
> :book: ***O que é um conjunto de chaves?*** Um conjunto de chaves é um grupo de chaves pré-geradas. As hardware wallets modernas criam molhos de chaves usando novos endereços hierárquicos com base na seed original. A ideia destes molhos de chaves é facilitar os requisitos de backup das carteiras. Isso permitia que um usuário gerasse um conjunto de chaves e fizesse backup da carteira imediatamente, ao invés de exigir backups após a criação de cada novo endereço. O conceito também tem se mostrado muito útil na atualidade, pois permite a importação de todo um conjunto de endereços de um dispositivo para outro.
|
||||
|
||||
Os valores retornados pelo comando ```getkeypool``` são os mesmos tipos de descritores que aprendemos na seção [§3.5: Compreendendo o Descritor](03_5_Understanding_the_Descriptor.md). Na época, dissemos que eram úteis para mover endereços entre máquinas diferentes. Aqui está o exemplo da vida real: mover endereços de uma hardware wallet para o node do Bitcoin Core, de modo que nossa máquina conectada à rede possa vigiar as chaves pertencentes à carteira da hardware wallet.
|
||||
|
||||
Assim como aprendemos na seção [§3.5](03_5_Understanding_the_Descriptor.md), podemos examinar esses descritores com o comando RPC ```getdescriptorinfo```:
|
||||
```
|
||||
$ bitcoin-cli getdescriptorinfo "wpkh([9a1d520b/84h/1h/0h]tpubDD7KTtoGzK9GuWUQcr1uTJazsAkqoXhdrwGXWVix6nPpNZmSbagZWD4QSaMsyK8YohAirGDPrWdRiEpKzTFB7DrTrqfzHCn7yi5EsqeR93S/0/*)#qttxy592"
|
||||
{
|
||||
"descriptor": "wpkh([9a1d520b/84'/1'/0']tpubDD7KTtoGzK9GuWUQcr1uTJazsAkqoXhdrwGXWVix6nPpNZmSbagZWD4QSaMsyK8YohAirGDPrWdRiEpKzTFB7DrTrqfzHCn7yi5EsqeR93S/0/*)#n65e7wjf",
|
||||
"checksum": "qttxy592",
|
||||
"isrange": true,
|
||||
"issolvable": true,
|
||||
"hasprivatekeys": false
|
||||
}
|
||||
```
|
||||
Como era de se esperar, _não_ temos ```privatekeys```, porque as hardware wallets as mantém.
|
||||
|
||||
Com os descritores em mãos, podemos importar as chaves para nossa nova carteira ```ledger``` usando o comando RPC ```importmulti``` que já usamos na seção [§3.5](03_5_Understanding_the_Descriptor.md). Nesse caso, basta colocar toda a resposta que recebemos do HWI entre ```'``` s.
|
||||
```
|
||||
$ bitcoin-cli -rpcwallet=ledger importmulti '[{"desc": "wpkh([9a1d520b/84h/1h/0h]tpubDD7KTtoGzK9GuWUQcr1uTJazsAkqoXhdrwGXWVix6nPpNZmSbagZWD4QSaMsyK8YohAirGDPrWdRiEpKzTFB7DrTrqfzHCn7yi5EsqeR93S/0/*)#qttxy592", "range": [0, 1000], "timestamp": "now", "internal": false, "keypool": true, "active": true, "watchonly": true}, {"desc": "wpkh([9a1d520b/84h/1h/0h]tpubDD7KTtoGzK9GuWUQcr1uTJazsAkqoXhdrwGXWVix6nPpNZmSbagZWD4QSaMsyK8YohAirGDPrWdRiEpKzTFB7DrTrqfzHCn7yi5EsqeR93S/1/*)#3lw8ep4j", "range": [0, 1000], "timestamp": "now", "internal": true, "keypool": true, "active": true, "watchonly": true}]'
|
||||
[
|
||||
{
|
||||
"success": true
|
||||
},
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
]
|
||||
```
|
||||
(Observe que HWI aproveita o caminho de derivação com ```h```s para mostrar as derivações ao invés dos ```'```s, além de ter calculado a soma da verificação corretamente, para que não tenhamos que fazer citações massivas como fizemos na seção §3.5.)
|
||||
|
||||
Agora, _poderíamos_ listar todos os endereços apenas para observar que recebemos o valor, usando o comando ```getaddressesbylabel```. Todos os 1000 endereços de recebimento estão ali, na carteira ```ledger```!
|
||||
```
|
||||
$ bitcoin-cli -rpcwallet=ledger getaddressesbylabel "" | more
|
||||
{
|
||||
"tb1qqqvnezljtmc9d7x52udpc0m9zgl9leugd2ur7y": {
|
||||
"purpose": "receive"
|
||||
},
|
||||
"tb1qqzvrm6hujdt93qctuuev5qc4499tq9fdk0prwf": {
|
||||
"purpose": "receive"
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
## Recebendo uma Transação
|
||||
|
||||
Obviamente, receber uma transação é bem simples. Usamos o ```getnewaddress``` para solicitar um daqueles endereços importados:
|
||||
```
|
||||
$ bitcoin-cli -rpcwallet=ledger getnewaddress
|
||||
tb1qqqvnezljtmc9d7x52udpc0m9zgl9leugd2ur7y
|
||||
```
|
||||
Então enviamos o dinheiro para ele.
|
||||
|
||||
O poder do HWI é que podemos observar os pagamentos do nosso node Bitcoin Core, ao invés de termos que conectar nossa hardware wallet e consultá-la.
|
||||
```
|
||||
$ bitcoin-cli -rpcwallet=ledger listunspent
|
||||
[
|
||||
{
|
||||
"txid": "c733533eb1c052242f9ed89cd8927aedb41852156e684634ee7c74028774e595",
|
||||
"vout": 1,
|
||||
"address": "tb1q948388a23pfsf52kz6skd5k4z4627jja2evztr",
|
||||
"label": "",
|
||||
"scriptPubKey": "00142d4f139faa885304d15616a166d2d51574af4a5d",
|
||||
"amount": 0.01000000,
|
||||
"confirmations": 12,
|
||||
"spendable": false,
|
||||
"solvable": true,
|
||||
"desc": "wpkh([9a1d520b/84'/1'/0'/0/0]02a013cf9c4b5f5689d9253036a3e477cf98689626f7814c94f092726f11b741ab)#9za8hlvk",
|
||||
"safe": true
|
||||
},
|
||||
{
|
||||
"txid": "5b3c4aeb811f9a119fd633b12a6927415cc61b8654628df58e9141cab804bab8",
|
||||
"vout": 0,
|
||||
"address": "tb1qqqvnezljtmc9d7x52udpc0m9zgl9leugd2ur7y",
|
||||
"label": "",
|
||||
"scriptPubKey": "001400193c8bf25ef056f8d4571a1c3f65123e5fe788",
|
||||
"amount": 0.01000000,
|
||||
"confirmations": 1,
|
||||
"spendable": false,
|
||||
"solvable": true,
|
||||
"desc": "wpkh([9a1d520b/84'/1'/0'/0/569]030168d9482e2b02d7027fb4a89edc54adaa1adf709334f647d0a1b0533828aec5)#sx9haake",
|
||||
"safe": true
|
||||
}
|
||||
]
|
||||
```
|
||||
## Criando uma Transação com PSBT
|
||||
|
||||
Observar e receber pagamentos é apenas metade da batalha. Também podemos fazer pagamentos usando contas mantidas em nossa hardware wallet. Este é o quarto exemplo da vida real usando as PSBTs, de acordo com o processo descrito na seção [§7.1: Criando uma Transação Bitcoin Parcialmente Assinada](7_1_Creating_a_Partially_Signed_Bitcoin_Transaction.md).
|
||||
|
||||
Os comandos funcionam exatamente da mesma forma. Nesse caso, usamos o ```walletcreatefundedpsbt``` para formar nossa PSBT porque estamos em uma situação em que não nos importamos com quais UTXOs são usados:
|
||||
```
|
||||
$ bitcoin-cli -named -rpcwallet=ledger walletcreatefundedpsbt inputs='''[]''' outputs='''[{"tb1qcaedd724gts3aug73m78c7nfsv9d8zs9q6h2kd":0.015}]'''
|
||||
{
|
||||
"psbt": "cHNidP8BAJoCAAAAAri6BLjKQZGO9Y1iVIYbxlxBJ2kqsTPWnxGaH4HrSjxbAAAAAAD+////leV0hwJ0fO40RmhuFVIYtO16ktic2J4vJFLAsT5TM8cBAAAAAP7///8CYOMWAAAAAAAWABTHctb5VULhHvEejvx8emmDCtOKBU+gBwAAAAAAFgAU9Ojd5ds3CJi1fIRWbj92CYhQgX0AAAAAAAEBH0BCDwAAAAAAFgAUABk8i/Je8Fb41FcaHD9lEj5f54giBgMBaNlILisC1wJ/tKie3FStqhrfcJM09kfQobBTOCiuxRiaHVILVAAAgAEAAIAAAACAAAAAADkCAAAAAQEfQEIPAAAAAAAWABQtTxOfqohTBNFWFqFm0tUVdK9KXSIGAqATz5xLX1aJ2SUwNqPkd8+YaJYm94FMlPCScm8Rt0GrGJodUgtUAACAAQAAgAAAAIAAAAAAAAAAAAAAIgID2UK1nupSfXC81nmB65XZ+pYlJp/W6wNk5FLt5ZCSx6kYmh1SC1QAAIABAACAAAAAgAEAAAABAAAAAA==",
|
||||
"fee": 0.00000209,
|
||||
"changepos": 1
|
||||
}
|
||||
```
|
||||
Podemos observar a PSBT e verificar se ela parece correta:
|
||||
```
|
||||
$ psbt="cHNidP8BAJoCAAAAAri6BLjKQZGO9Y1iVIYbxlxBJ2kqsTPWnxGaH4HrSjxbAAAAAAD+////leV0hwJ0fO40RmhuFVIYtO16ktic2J4vJFLAsT5TM8cBAAAAAP7///8CYOMWAAAAAAAWABTHctb5VULhHvEejvx8emmDCtOKBU+gBwAAAAAAFgAU9Ojd5ds3CJi1fIRWbj92CYhQgX0AAAAAAAEBH0BCDwAAAAAAFgAUABk8i/Je8Fb41FcaHD9lEj5f54giBgMBaNlILisC1wJ/tKie3FStqhrfcJM09kfQobBTOCiuxRiaHVILVAAAgAEAAIAAAACAAAAAADkCAAAAAQEfQEIPAAAAAAAWABQtTxOfqohTBNFWFqFm0tUVdK9KXSIGAqATz5xLX1aJ2SUwNqPkd8+YaJYm94FMlPCScm8Rt0GrGJodUgtUAACAAQAAgAAAAIAAAAAAAAAAAAAAIgID2UK1nupSfXC81nmB65XZ+pYlJp/W6wNk5FLt5ZCSx6kYmh1SC1QAAIABAACAAAAAgAEAAAABAAAAAA=="
|
||||
|
||||
$ bitcoin-cli decodepsbt $psbt
|
||||
{
|
||||
"tx": {
|
||||
"txid": "45f996d4ff8c9e9ab162f611c5b6ad752479ede9780f9903bdc80cd96619676d",
|
||||
"hash": "45f996d4ff8c9e9ab162f611c5b6ad752479ede9780f9903bdc80cd96619676d",
|
||||
"version": 2,
|
||||
"size": 154,
|
||||
"vsize": 154,
|
||||
"weight": 616,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "5b3c4aeb811f9a119fd633b12a6927415cc61b8654628df58e9141cab804bab8",
|
||||
"vout": 0,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967294
|
||||
},
|
||||
{
|
||||
"txid": "c733533eb1c052242f9ed89cd8927aedb41852156e684634ee7c74028774e595",
|
||||
"vout": 1,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"sequence": 4294967294
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.01500000,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 c772d6f95542e11ef11e8efc7c7a69830ad38a05",
|
||||
"hex": "0014c772d6f95542e11ef11e8efc7c7a69830ad38a05",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"tb1qcaedd724gts3aug73m78c7nfsv9d8zs9q6h2kd"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00499791,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 f4e8dde5db370898b57c84566e3f76098850817d",
|
||||
"hex": "0014f4e8dde5db370898b57c84566e3f76098850817d",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"tb1q7n5dmewmxuyf3dtus3txu0mkpxy9pqtacuprak"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"unknown": {
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"witness_utxo": {
|
||||
"amount": 0.01000000,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 00193c8bf25ef056f8d4571a1c3f65123e5fe788",
|
||||
"hex": "001400193c8bf25ef056f8d4571a1c3f65123e5fe788",
|
||||
"type": "witness_v0_keyhash",
|
||||
"address": "tb1qqqvnezljtmc9d7x52udpc0m9zgl9leugd2ur7y"
|
||||
}
|
||||
},
|
||||
"bip32_derivs": [
|
||||
{
|
||||
"pubkey": "030168d9482e2b02d7027fb4a89edc54adaa1adf709334f647d0a1b0533828aec5",
|
||||
"master_fingerprint": "9a1d520b",
|
||||
"path": "m/84'/1'/0'/0/569"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"witness_utxo": {
|
||||
"amount": 0.01000000,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 2d4f139faa885304d15616a166d2d51574af4a5d",
|
||||
"hex": "00142d4f139faa885304d15616a166d2d51574af4a5d",
|
||||
"type": "witness_v0_keyhash",
|
||||
"address": "tb1q948388a23pfsf52kz6skd5k4z4627jja2evztr"
|
||||
}
|
||||
},
|
||||
"bip32_derivs": [
|
||||
{
|
||||
"pubkey": "02a013cf9c4b5f5689d9253036a3e477cf98689626f7814c94f092726f11b741ab",
|
||||
"master_fingerprint": "9a1d520b",
|
||||
"path": "m/84'/1'/0'/0/0"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
},
|
||||
{
|
||||
"bip32_derivs": [
|
||||
{
|
||||
"pubkey": "03d942b59eea527d70bcd67981eb95d9fa9625269fd6eb0364e452ede59092c7a9",
|
||||
"master_fingerprint": "9a1d520b",
|
||||
"path": "m/84'/1'/0'/1/1"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"fee": 0.00000209
|
||||
}
|
||||
```
|
||||
E, como de costume, `analisepsbt` mostrará o quão longe você chegou:
|
||||
```
|
||||
$ bitcoin-cli analyzepsbt $psbt
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"has_utxo": true,
|
||||
"is_final": false,
|
||||
"next": "signer",
|
||||
"missing": {
|
||||
"signatures": [
|
||||
"00193c8bf25ef056f8d4571a1c3f65123e5fe788"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"has_utxo": true,
|
||||
"is_final": false,
|
||||
"next": "signer",
|
||||
"missing": {
|
||||
"signatures": [
|
||||
"2d4f139faa885304d15616a166d2d51574af4a5d"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"estimated_vsize": 208,
|
||||
"estimated_feerate": 0.00001004,
|
||||
"fee": 0.00000209,
|
||||
"next": "signer"
|
||||
}
|
||||
```
|
||||
Como importamos aquele conjunto de chaves, o ```bitcoin-cli``` tem todas as informações de que precisa para preencher as entradas, porém, ele não pode assinar porque as chaves privadas são mantidas na hardware wallet.
|
||||
|
||||
É aí que entra o HWI, com o comando ```signtx```. Basta que enviemos a PSBT junto:
|
||||
```
|
||||
$ hwi -f 9a1d520b signtx $psbt
|
||||
```
|
||||
É provável que tenhamos de mexer em nossa hardware wallet neste momento. O dispositivo provavelmente pedirá a confirmação das entradas, as saídas e a taxa. Quando terminar, ele deve retornar uma nova PSBT.
|
||||
|
||||
```
|
||||
{"psbt": "cHNidP8BAJoCAAAAAri6BLjKQZGO9Y1iVIYbxlxBJ2kqsTPWnxGaH4HrSjxbAAAAAAD+////leV0hwJ0fO40RmhuFVIYtO16ktic2J4vJFLAsT5TM8cBAAAAAP7///8CYOMWAAAAAAAWABTHctb5VULhHvEejvx8emmDCtOKBU+gBwAAAAAAFgAU9Ojd5ds3CJi1fIRWbj92CYhQgX0AAAAAAAEBH0BCDwAAAAAAFgAUABk8i/Je8Fb41FcaHD9lEj5f54giAgMBaNlILisC1wJ/tKie3FStqhrfcJM09kfQobBTOCiuxUcwRAIgAxkQlk2fqEMxvP54WWyiFhlfSul9sd4GzKDhfGpmlewCIHYej3zXWWMgWI6rixxQw9yzGozDaFPqQNNIvcFPk+lfASIGAwFo2UguKwLXAn+0qJ7cVK2qGt9wkzT2R9ChsFM4KK7FGJodUgtUAACAAQAAgAAAAIAAAAAAOQIAAAABAR9AQg8AAAAAABYAFC1PE5+qiFME0VYWoWbS1RV0r0pdIgICoBPPnEtfVonZJTA2o+R3z5holib3gUyU8JJybxG3QatHMEQCIH5t6T2yufUP7glYZ8YH0/PhDFpotSmjgZUhvj6GbCFIAiBcgXzyYl7IjYuaF3pJ7AgW1rLYkjeCJJ2M9pVUrq5vFwEiBgKgE8+cS19WidklMDaj5HfPmGiWJveBTJTwknJvEbdBqxiaHVILVAAAgAEAAIAAAACAAAAAAAAAAAAAACICA9lCtZ7qUn1wvNZ5geuV2fqWJSaf1usDZORS7eWQksepGJodUgtUAACAAQAAgAAAAIABAAAAAQAAAAA="}
|
||||
$ psbt_f="cHNidP8BAJoCAAAAAri6BLjKQZGO9Y1iVIYbxlxBJ2kqsTPWnxGaH4HrSjxbAAAAAAD+////leV0hwJ0fO40RmhuFVIYtO16ktic2J4vJFLAsT5TM8cBAAAAAP7///8CYOMWAAAAAAAWABTHctb5VULhHvEejvx8emmDCtOKBU+gBwAAAAAAFgAU9Ojd5ds3CJi1fIRWbj92CYhQgX0AAAAAAAEBH0BCDwAAAAAAFgAUABk8i/Je8Fb41FcaHD9lEj5f54giAgMBaNlILisC1wJ/tKie3FStqhrfcJM09kfQobBTOCiuxUcwRAIgAxkQlk2fqEMxvP54WWyiFhlfSul9sd4GzKDhfGpmlewCIHYej3zXWWMgWI6rixxQw9yzGozDaFPqQNNIvcFPk+lfASIGAwFo2UguKwLXAn+0qJ7cVK2qGt9wkzT2R9ChsFM4KK7FGJodUgtUAACAAQAAgAAAAIAAAAAAOQIAAAABAR9AQg8AAAAAABYAFC1PE5+qiFME0VYWoWbS1RV0r0pdIgICoBPPnEtfVonZJTA2o+R3z5holib3gUyU8JJybxG3QatHMEQCIH5t6T2yufUP7glYZ8YH0/PhDFpotSmjgZUhvj6GbCFIAiBcgXzyYl7IjYuaF3pJ7AgW1rLYkjeCJJ2M9pVUrq5vFwEiBgKgE8+cS19WidklMDaj5HfPmGiWJveBTJTwknJvEbdBqxiaHVILVAAAgAEAAIAAAACAAAAAAAAAAAAAACICA9lCtZ7qUn1wvNZ5geuV2fqWJSaf1usDZORS7eWQksepGJodUgtUAACAAQAAgAAAAIABAAAAAQAAAAA="
|
||||
```
|
||||
Ao analisá-la, veremos que estamos prontos para finalizá-la:
|
||||
```
|
||||
$ bitcoin-cli analyzepsbt $psbt_f
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"has_utxo": true,
|
||||
"is_final": false,
|
||||
"next": "finalizer"
|
||||
},
|
||||
{
|
||||
"has_utxo": true,
|
||||
"is_final": false,
|
||||
"next": "finalizer"
|
||||
}
|
||||
],
|
||||
"estimated_vsize": 208,
|
||||
"estimated_feerate": 0.00001004,
|
||||
"fee": 0.00000209,
|
||||
"next": "finalizer"
|
||||
}
|
||||
```
|
||||
Neste momento, estamos de volta a terras já conhecidas:
|
||||
```
|
||||
$ bitcoin-cli finalizepsbt $psbt_f
|
||||
{
|
||||
"hex": "02000000000102b8ba04b8ca41918ef58d6254861bc65c4127692ab133d69f119a1f81eb4a3c5b0000000000feffffff95e5748702747cee3446686e155218b4ed7a92d89cd89e2f2452c0b13e5333c70100000000feffffff0260e3160000000000160014c772d6f95542e11ef11e8efc7c7a69830ad38a054fa0070000000000160014f4e8dde5db370898b57c84566e3f76098850817d024730440220031910964d9fa84331bcfe78596ca216195f4ae97db1de06cca0e17c6a6695ec0220761e8f7cd7596320588eab8b1c50c3dcb31a8cc36853ea40d348bdc14f93e95f0121030168d9482e2b02d7027fb4a89edc54adaa1adf709334f647d0a1b0533828aec50247304402207e6de93db2b9f50fee095867c607d3f3e10c5a68b529a3819521be3e866c214802205c817cf2625ec88d8b9a177a49ec0816d6b2d8923782249d8cf69554aeae6f17012102a013cf9c4b5f5689d9253036a3e477cf98689626f7814c94f092726f11b741ab00000000",
|
||||
"complete": true
|
||||
}
|
||||
$ hex=02000000000102b8ba04b8ca41918ef58d6254861bc65c4127692ab133d69f119a1f81eb4a3c5b0000000000feffffff95e5748702747cee3446686e155218b4ed7a92d89cd89e2f2452c0b13e5333c70100000000feffffff0260e3160000000000160014c772d6f95542e11ef11e8efc7c7a69830ad38a054fa0070000000000160014f4e8dde5db370898b57c84566e3f76098850817d024730440220031910964d9fa84331bcfe78596ca216195f4ae97db1de06cca0e17c6a6695ec0220761e8f7cd7596320588eab8b1c50c3dcb31a8cc36853ea40d348bdc14f93e95f0121030168d9482e2b02d7027fb4a89edc54adaa1adf709334f647d0a1b0533828aec50247304402207e6de93db2b9f50fee095867c607d3f3e10c5a68b529a3819521be3e866c214802205c817cf2625ec88d8b9a177a49ec0816d6b2d8923782249d8cf69554aeae6f17012102a013cf9c4b5f5689d9253036a3e477cf98689626f7814c94f092726f11b741ab00000000
|
||||
$ bitcoin-cli sendrawtransaction $hex
|
||||
45f996d4ff8c9e9ab162f611c5b6ad752479ede9780f9903bdc80cd96619676d
|
||||
```
|
||||
Pronto, enviamos um saldo com sucesso usando as chaves privadas de nossa hardware wallet!
|
||||
|
||||
## Aprendendo Outros Comandos HWI
|
||||
|
||||
Existem vários outros comandos disponíveis para usar com o HWI. No momento em que este artigo foi escrito, são eles:
|
||||
```
|
||||
numerate,getmasterxpub,signtx,getxpub,signmessage,getkeypool,getdescriptors,displayaddress,setup,wipe,restore,backup,promptpin,togglepassphrase,sendpin
|
||||
```
|
||||
## Resumo: Integrando com Hardware Wallets
|
||||
|
||||
As hardware wallets podem oferecer melhor proteção, mantendo nossas chaves privadas offline, protegidas no hardware. Felizmente, ainda há uma maneira de interagir com elas usando o ```bitcoin-cli```. Basta instalar o HWI e ele permitirá (1) importar as chaves públicas para observarmos; e (2) assinar transações usando nossa hardware wallet.
|
||||
|
||||
> :fire: ***Qual é o poder do HWI?*** O HWI nos permite interagir com hardware wallets usando todos os comandos do ```bitcoin-cli``` que aprendemos até agora. Podemos fazer transações brutas de qualquer tipo e enviar PSBTs para hardware wallets para serem assinadas. Assim, temos todo o poder do Bitcoin Core, mas também temos a segurança de um dispositivo hardware.
|
||||
|
||||
## O Que Vem Depois?
|
||||
|
||||
Vamos expandir ainda mais as transações de Bitcoin com o [Capítulo Oito: Expandindo Transações no Bitcoin de Outras Maneiras](08_0_Expanding_Bitcoin_Transactions_Other.md).
|
Loading…
x
Reference in New Issue
Block a user