mirror of
https://github.com/ChristopherA/Learning-Bitcoin-from-the-Command-Line.git
synced 2025-06-07 16:06:26 +00:00
Chapter 17 translation concluded
This commit is contained in:
parent
bb6dd6cfde
commit
7491a68ed7
26
pt/17_0_Talking_to_Bitcoind_Other.md
Normal file
26
pt/17_0_Talking_to_Bitcoind_Other.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Capítulo 17: Conversando com o Bitcoind com Outras Linguagens
|
||||
|
||||
Agora devemos ter uma base sólida para trabalhar com Bitcoin usando a linguagem C, não apenas usando as bibliotecas RPC, JSON e ZMQ para interagir diretamente com o `bitcoind`, mas também, utilizando as bibliotecas Libwally para complementar esse trabalho. O C é uma ótima linguagem para prototipagem e abstração, porém provavelmente não é o que estamos usando para programar. Este capítulo, portanto, faz uma apresentação rápida por seis outras linguagens de programação, demonstrando a funcionalidade mais básica do Bitcoin em cada uma, permitindo que possamos expandir as lições da linha de comando e do C para a linguagem de programação que desejarmos escolha.
|
||||
|
||||
Cada uma das seções contém aproximadamente as mesmas informações, o foco será: Criar uma conexão RPC, examinar a carteira, criando um novo endereço e criar uma transação. No entanto, há alguma mudança entre as linguagens, mostrando diferentes aspectos dos comandos RPC do Bitcoin em diferentes exemplos. Em particular, algumas linguagens usam a metodologia fácil do `sendtoaddress`, enquanto outras usam uma metodologia mais difícil para a criação de uma transação bruta do zero.
|
||||
|
||||
## Objetivos deste capítulo
|
||||
|
||||
Depois de trabalhar neste capítulo, um desenvolvedor será capaz de:
|
||||
|
||||
* Preparar ambientes de desenvolvimento para o Bitcoin para uma variedade de linguagens;
|
||||
* Usar as funções da carteira em várias linguagens;
|
||||
* Usar funções de transação em uma variedade de linguagens.
|
||||
|
||||
Os objetivos secundários do capítulo incluem a capacidade de:
|
||||
|
||||
* Saiber mais sobre o Bitcoin RPC por meio de interações com uma variedade de linguagens.
|
||||
|
||||
## Tabela de Conteúdo
|
||||
|
||||
* [Seção 1: Acessando o Bitcoind com Go](17_1_Accessing_Bitcoind_with_Go.md)
|
||||
* [Seção 2: Acessando o Bitcoind com Java](17_2_Accessing_Bitcoind_with_Java.md)
|
||||
* [Seção 3: Acessando o Bitcoind com NodeJS](17_3_Accessing_Bitcoind_with_NodeJS.md)
|
||||
* [Seção 4: Acessando o Bitcoind com Python](17_4_Accessing_Bitcoind_with_Python.md)
|
||||
* [Seção 5: Acessando o Bitcoind com Rust](17_5_Accessing_Bitcoind_with_Rust.md)
|
||||
* [Seção 6: Acessando o Bitcoind com Swift](17_6_Accessing_Bitcoind_with_Swift.md)
|
421
pt/17_1_Accessing_Bitcoind_with_Go.md
Normal file
421
pt/17_1_Accessing_Bitcoind_with_Go.md
Normal file
@ -0,0 +1,421 @@
|
||||
# 17.1: Acessando o Bitcoind com Go
|
||||
|
||||
> :information_source: **NOTA:** Esta seção foi adicionada recentemente ao curso e é um rascunho inicial que ainda pode estar aguardando revisão.
|
||||
|
||||
Esta seção explica como interagir com o `bitcoind` usando a linguagem de programação Go e o [btcd rpcclient](https://github.com/btcsuite/btcd/tree/master/rpcclient). É importante observar que ele tem algumas peculiaridades e limitações.
|
||||
|
||||
## Configurando o Go
|
||||
|
||||
Para nos preparamos para o uso do Go em nossa máquina UNIX, primeiro precisamos instalar o curl, caso ainda não o tenhamos feito:
|
||||
|
||||
```
|
||||
$ sudo apt install curl
|
||||
```
|
||||
|
||||
Então, vamos ler a [página de downloads do Go](https://golang.org/dl/), para obtermos o link para o download mais recente e posteriormente fazer o download usando `curl`. Para uma configuração Debian, vamos querer usar a versão `linux-amd64`:
|
||||
|
||||
```
|
||||
$ curl -O https://dl.google.com/go/go1.15.1.linux-amd64.tar.gz
|
||||
```
|
||||
Assim que terminarmos o download, comparamos o hash com o hash na [página de downloads do Go](https://golang.org/dl/):
|
||||
|
||||
```
|
||||
$ sha256sum go1.15.1.linux-amd64.tar.gz
|
||||
70ac0dbf60a8ee9236f337ed0daa7a4c3b98f6186d4497826f68e97c0c0413f6 go1.15.1.linux-amd64.tar.gz
|
||||
```
|
||||
|
||||
Os hashes devem corresponder. Em caso afirmativo, extraímos o tarball e instalamos o Go em nosso sistema:
|
||||
```
|
||||
$ tar xfv go1.15.1.linux-amd64.tar.gz
|
||||
$ sudo chown -R root:root ./go
|
||||
$ sudo mv go /usr/local
|
||||
```
|
||||
|
||||
Agora precisamos criar um caminho no Go para especificar nosso ambiente. Abra o arquivo `~ / .profile` com o editor de texto mais utilizado por nós e vamos escolher e adicionar o seguinte ao final dele:
|
||||
|
||||
```
|
||||
export GOPATH=$HOME/work
|
||||
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
|
||||
```
|
||||
|
||||
Em seguida, atualizamos nosso perfil:
|
||||
|
||||
```
|
||||
$ source ~/.profile
|
||||
```
|
||||
|
||||
Por fim, vamos criar o diretório para o nosso espaço de trabalho Go:
|
||||
|
||||
```
|
||||
$ mkdir $HOME/work
|
||||
```
|
||||
|
||||
### Configurando o `btcd` e o `rpcclient`
|
||||
|
||||
Usaremos o `rpcclient` que advém do `btcd`, uma implementação Bitcoin escrita em Go. Embora o `rpcclient` tenha sido originalmente projetado para funcionar com o full node `btcd` do Bitcoin, ele também funciona com o Bitcoin Core. Ele tem algumas peculiaridades que veremos mais adiante.
|
||||
|
||||
Podemos usar o ```go get``` para baixá-lo:
|
||||
|
||||
```
|
||||
$ go get github.com/btcsuite/btcd/rpcclient
|
||||
```
|
||||
|
||||
Para testar seu funcionamento, vmaos navegar até o diretório com os exemplos do Bitcoin Core:
|
||||
|
||||
```
|
||||
$ cd $GOPATH/src/github.com/btcsuite/btcd/rpcclient/examples/bitcoincorehttp
|
||||
```
|
||||
|
||||
Vamos modificar o arquivo `main.go` e inserir os detalhes associados à configuração do Bitcoin Core, que pode ser encontrado no caminho `~ /.bitcoin/bitcoin.conf`:
|
||||
|
||||
```
|
||||
Host: "localhost:18332",
|
||||
User: "StandUp",
|
||||
Pass: "6305f1b2dbb3bc5a16cd0f4aac7e1eba",
|
||||
```
|
||||
> **MAINNET VS TESTNET:** A porta seria 8332 para uma configuração na Mainnet.
|
||||
|
||||
Agora podemos executar um teste:
|
||||
```
|
||||
$ go run main.go
|
||||
```
|
||||
|
||||
Devemos ver a contagem de blocos impressa:
|
||||
|
||||
```
|
||||
2020/09/01 11:41:24 Block count: 1830861
|
||||
```
|
||||
|
||||
### Criando um projeto `rpcclient`
|
||||
|
||||
Normalmente, iríamos criar projetos no diretório `~/work/src/myproject/bitcoin`:
|
||||
```
|
||||
$ mkdir -p ~/work/src/myproject/bitcoin
|
||||
$ cd ~/work/src/myproject/bitcoin
|
||||
```
|
||||
Cada projeto deve ter os seguintes `imports`:
|
||||
```
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
)
|
||||
```
|
||||
Esta declaração `import` permite que importemos bibliotecas relevantes. Para cada exemplo aqui, precisaremos importar `"log","fmt"` e `"github.com/btcsuite/btcd/rpcclient"`. Podemos precisar importar bibliotecas adicionais em alguns exemplos.
|
||||
|
||||
* O `log` é usado para mostrar mensagens de erro na tela. Após cada vez que o node Bitcoin for chamado, uma instrução `if` irá verificar se há algum erro. Se houver erros, o `log` é usado para imprimi-los;
|
||||
* O `fmt` é usado para imprimir a saída;
|
||||
* O `rpcclient` é obviamente a biblioteca do `rpcclient`;
|
||||
|
||||
## Construindo nossa conexão
|
||||
|
||||
Cada função do `bitcoind` no Go começa com a criação da conexão RPC, usando a função `ConnConfig`:
|
||||
```
|
||||
connCfg := &rpcclient.ConnConfig{
|
||||
Host: "localhost:18332",
|
||||
User: "StandUp",
|
||||
Pass: "431451790e3eee1913115b9dd2fbf0ac",
|
||||
HTTPPostMode: true,
|
||||
DisableTLS: true,
|
||||
}
|
||||
client, err := rpcclient.New(connCfg, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Shutdown()
|
||||
```
|
||||
Os parâmetros da `connCfg` permitem que escolhamos a porta Bitcoin RPC, nome de usuário, senha e se estamos usando a testnet ou mainnet.
|
||||
|
||||
> **NOTA:** Novamente, precisamos nos certificar de substituir o `User` e o `Pass` com aquele encontrado no nosso `~/.bitcoin/bitcon.conf`.
|
||||
|
||||
A função `rpcclient.New(connCfg, nil)` configura o`client` para nos conectarmos ao nosso node de Bitcoin.
|
||||
|
||||
A linha `defer client.Shutdown()` é para desconectar do nosso node Bitcoin, uma vez que a função `main()` termina de ser executada. Após a linha `defer client.Shutdown()` é onde as coisas interessantes acontecem, e será muito fácil de utilizar. Isso porque o `rpcclient` ajuda a transformar os comandos `bitcoin-cli` em funções, usando PascalCase. Por exemplo, `bitcoin-cli getblockcount` se transformará em `client.GetBlockCount` no Go.
|
||||
|
||||
### Fazendo uma chamada RPC
|
||||
|
||||
Agora, tudo o que é necessário é fazer uma chamada informativa como `GetBlockCount` ou` GetBlockHash` usando nosso `client`:
|
||||
```
|
||||
blockCount, err := client.GetBlockCount()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
blockHash, err := client.GetBlockHash(blockCount)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%d\n", blockCount)
|
||||
fmt.Printf("%s\n", blockHash.String())
|
||||
```
|
||||
|
||||
### Fazendo uma chamada RPC com argumentos
|
||||
|
||||
As funções `rpcclient` também podem receber entradas, por exemplo, `client.GetBlockHash(blockCount)` recebe a contagem de blocos como uma entrada. O `client.GetBlockHash (blockCount)` de cima seria parecido com um comando `bitcoin-cli`:
|
||||
```
|
||||
$ bitcoin-cli getblockhash 1830868
|
||||
00000000000002d53b6b9bba4d4e7dc44a79cebd1024d1bcfb9b3cc07d6cad9c
|
||||
```
|
||||
No entanto, uma peculiaridade com hashes no `rpcclient` é que normalmente eles irão ser mostrados em uma codificação diferente se imprimirmos normalmente com o ` blockHash`. Para imprimi-los como uma string, precisamos usar o `blockHash.String()`.
|
||||
|
||||
### Executando nosso código
|
||||
|
||||
Podemos baixar o código completo do [diretório src](src / 17_1_blockinfo.go).
|
||||
|
||||
Podemos então, executar:
|
||||
```
|
||||
$ go run blockinfo.go
|
||||
1830868
|
||||
00000000000002d53b6b9bba4d4e7dc44a79cebd1024d1bcfb9b3cc07d6cad9c
|
||||
```
|
||||
|
||||
O último número do bloco junto com nosso hash deve ser impresso.
|
||||
|
||||
## Pesquisando os fundos
|
||||
|
||||
Devido às limitações do `btcd` no `rpcclient`, não pode fazer o uso da função `getwalletinfo`. No entanto, podemos usar o RPC `getbalance`:
|
||||
|
||||
```
|
||||
wallet, err := client.GetBalance("*")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(wallet)
|
||||
```
|
||||
O `client.GetBalance("*")` requer a entrada `"*"`, devido a uma peculiaridade do `btcd`. O asterisco significa que desejamos obter o saldo de todas as nossas carteiras.
|
||||
|
||||
Se executarmos [o código src](src / 17_1_getbalance.go), deveremos obter uma saída semelhante a esta:
|
||||
```
|
||||
$ go run getbalance.go
|
||||
0.000689 BTC
|
||||
```
|
||||
|
||||
## Criando um endereço
|
||||
|
||||
Podemos gerar endereços em Go, mas não podemos especificar o tipo do endereço:
|
||||
|
||||
Isso requer o uso de uma função especial `chaincfg`, para especificar para qual rede os endereços estão sendo criados. Esta especificação é necessária apenas durante a geração do endereço, por isso é usada apenas neste exemplo. Também podemos incluir isso nos demais exemplos, mas não é necessário.
|
||||
|
||||
Be sure to import ```"github.com/btcsuite/btcd/chaincfg"```:
|
||||
Vamos nos certificar de importar o `"github.com/btcsuite/btcd/chaincfg"`:
|
||||
```
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
)
|
||||
```
|
||||
Em seguida, vamos chamar o `connCfG` com o parâmetro `chaincfg.TestNet3Params.Name`:
|
||||
```
|
||||
connCfg := &rpcclient.ConnConfig{
|
||||
Host: "localhost:18332",
|
||||
User: "bitcoinrpc",
|
||||
Pass: "431451790e3eee1913115b9dd2fbf0ac",
|
||||
HTTPPostMode: true,
|
||||
DisableTLS: true,
|
||||
Params: chaincfg.TestNet3Params.Name,
|
||||
}
|
||||
client, err := rpcclient.New(connCfg, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Shutdown()
|
||||
```
|
||||
> **MAINNET VS TESTNET:** O `Params: chaincfg.TestNet3Params.Name` deve ser `Params: chaincfg.MainNetParams.Name,` na Mainnet.
|
||||
|
||||
Podemos então criar nosso endereço:
|
||||
```
|
||||
address, err := client.GetNewAddress("")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(address)
|
||||
```
|
||||
Uma peculiaridade com o `client.GetNewAddress("")` é que uma string vazia precisa ser incluída para que tudo funcione perfeitamente.
|
||||
|
||||
Executando o [the source](17_1_getaddress.go) teremos os seguintes resultados:
|
||||
```
|
||||
$ go run getaddress.go
|
||||
tb1qutkcj34pw0aq7n9wgp3ktmz780szlycwddfmza
|
||||
```
|
||||
|
||||
### Decodificando um endereço
|
||||
|
||||
Criando um endereço exigia um trabalho extra, ao especificar a blockchain correta. Usar um endereço também funcionará, porque teremos que decodificá-lo antes de usá-lo.
|
||||
|
||||
Isso significa que teremos que importar as bibliotecas `"github.com/btcsuite/btcutil"` e `"github.com/btcsuite/btcd/chaincfg"`.
|
||||
|
||||
* O `btcutil` permite que um endereço Bitcoin seja decodificado de uma forma que o `rpcclient` possa entender. Isso é necessário ao trabalhar com endereços no `rpcclient`;
|
||||
* O `chaincfg` é (novamente) usado para configurar nossa cadeia como a cadeia Testnet. Isso é necessário para a decodificação de endereços, pois os endereços usados na Mainnet e na Testnet são diferentes.
|
||||
|
||||
```
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
)
|
||||
```
|
||||
A variável defaultNet agora é usada para especificar se nosso node do Bitcoin que está na Testnet ou na Mainnet. Essa informação (e o objeto `btcutil`) é então usado para decodificar o endereço.
|
||||
|
||||
> **MAINNET VS TESTNET:** `&chaincfg.TestNet3Params` deve ser `& chaincfg.MainNetParams` na Mainnet.
|
||||
|
||||
```
|
||||
defaultNet := &chaincfg.TestNet3Params
|
||||
addr, err := btcutil.DecodeAddress("mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha", defaultNet)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
> **NOTA:** Precisamos alterar o endereço (`mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha`) para um que seja da nossa carteira. Podemos usar o `bitcoin-cli listunspent` para encontrar alguns endereços com fundos para o teste. Se quisermos ser realmente sofisticados, podemos modificar o código Go para obter um argumento e, em seguida, escrever um script que executa o `listunspent`, para depois salvarmos a informação em uma variável e executar o código Go nela.
|
||||
|
||||
Só depois disso usamos o RPC `getreceivedbyaddress`, no nosso endereço decodificado:
|
||||
```
|
||||
wallet, err := client.GetReceivedByAddress(addr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(wallet)
|
||||
```
|
||||
Ao executar [o código](src/17_1_getamountreceived.go), devemos obter uma saída semelhante a esta:
|
||||
```
|
||||
$ go run getamountreceived.go
|
||||
0.0085 BTC
|
||||
```
|
||||
|
||||
## Enviando uma transação
|
||||
|
||||
Agora temos todas as peças do quebra-cabeça no lugar para enviar uma transação. Vamos querer:
|
||||
|
||||
1. Importar as bibliotecas corretas, incluindo a `chaincfg` para especificar uma rede e o `btcutil` para decodificar um endereço;
|
||||
2. Escolher um endereço para enviar;
|
||||
3. Decodificar esse endereço;
|
||||
4. Executar o `sendtoaddress` para enviar fundos da maneira mais fácil.
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
)
|
||||
|
||||
func main() {
|
||||
connCfg := &rpcclient.ConnConfig{
|
||||
Host: "localhost:18332",
|
||||
User: "StandUp",
|
||||
Pass: "431451790e3eee1913115b9dd2fbf0ac",
|
||||
HTTPPostMode: true,
|
||||
DisableTLS: true,
|
||||
}
|
||||
client, err := rpcclient.New(connCfg, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Shutdown()
|
||||
|
||||
defaultNet := &chaincfg.TestNet3Params
|
||||
addr, err := btcutil.DecodeAddress("n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi", defaultNet)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
sent, err := client.SendToAddress(addr, btcutil.Amount(1e4))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(sent)
|
||||
}
|
||||
```
|
||||
Quando executamos [o código](src/17_1_sendtransaction.go), o txid da transação nos será retornado:
|
||||
|
||||
```
|
||||
$ go run sendtransaction.go
|
||||
9aa4cd6559e0d69059eae142c35bfe78b71a8084e1fcc2c74e2a9675e9e7489d
|
||||
```
|
||||
|
||||
### Pesquisando uma transação
|
||||
|
||||
Para consultar uma transação, como a que acabamos de enviar, precisaremos fazer mais uma vez algumas conversões, desta vez do txid. O `"github.com/btcsuite/btcd/chaincfg/chainhash"` é importado para permitir que os hashes sejam armazenados no código Go. O `chainhash.NewHashFromStr("hash")` converte um hash em uma string para um formato que funciona com o rpcclient.
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
func main() {
|
||||
connCfg := &rpcclient.ConnConfig{
|
||||
Host: "localhost:18332",
|
||||
User: "StandUp",
|
||||
Pass: "431451790e3eee1913115b9dd2fbf0ac",
|
||||
HTTPPostMode: true,
|
||||
DisableTLS: true,
|
||||
}
|
||||
client, err := rpcclient.New(connCfg, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Shutdown()
|
||||
|
||||
chash, err := chainhash.NewHashFromStr("1661ce322c128e053b8ea8fcc22d17df680d2052983980e2281d692b9b4ab7df")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
transactions, err := client.GetTransaction(chash)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(transactions)
|
||||
}
|
||||
```
|
||||
> **NOTA:** Novamente, vamos querer trocar o txid por um que realmente será reconhecido pelo nosso sistema.
|
||||
|
||||
Ao executar [o código](src/17_1_lookuptransaction.go), ele imprimirá os detalhes associados a uma transação, como nosso valor e quantas vezes foi confirmado:
|
||||
|
||||
```
|
||||
$ go run lookuptransaction.go
|
||||
{
|
||||
"amount": 0.00100000,
|
||||
"confirmations": 4817,
|
||||
"blockhash": "000000006628870b0a8a66abea9cf0d4e815c491f079e3fa9e658a87b5dc863a",
|
||||
"blockindex": 117,
|
||||
"blocktime": 1591857418,
|
||||
"txid": "1661ce322c128e053b8ea8fcc22d17df680d2052983980e2281d692b9b4ab7df",
|
||||
"walletconflicts": [
|
||||
],
|
||||
"time": 1591857343,
|
||||
"timereceived": 1591857343,
|
||||
"bip125-replaceable": "no",
|
||||
"details": [
|
||||
{
|
||||
"address": "mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha",
|
||||
"category": "receive",
|
||||
"amount": 0.00100000,
|
||||
"label": "",
|
||||
"vout": 0
|
||||
}
|
||||
],
|
||||
"hex": "02000000000101e9e8c3bd057d54e73baadc60c166860163b0e7aa60cab33a03e89fb44321f8d5010000001716001435c2aa3fc09ea53c3e23925c5b2e93b9119b2568feffffff02a0860100000000001976a914600c8c6a4abb0a502ea4de01681fe4fa1ca7800688ac65ec1c000000000017a91425b920efb2fde1a0277d3df11d0fd7249e17cf8587024730440220403a863d312946aae3f3ef0a57206197bc67f71536fb5f4b9ca71a7e226b6dc50220329646cf786cfef79d60de3ef54f702ab1073694022f0618731902d926918c3e012103e6feac9d7a8ad1ac6b36fb4c91c1c9f7fff1e7f63f0340e5253a0e4478b7b13f41fd1a00"
|
||||
}
|
||||
```
|
||||
|
||||
## Resumo: Acessando o Bitcoind com Go
|
||||
|
||||
Embora o `btcd` e o `rpcclient` tenham alguns limites, ainda podemos executar os principais comandos RPC no Go. A documentação para o `rpcclient` está disponível no [Godoc](https://godoc.org/github.com/btcsuite/btcd/rpcclient). Se a documentação não tiver o que procuramos, podemos consultar também o [repositório btcd](https://github.com/btcsuite/btcd). Geralmente é bem documentado e fácil de ler. Com base nesses exemplos, devemos ser capazes de incorporar o Bitcoin em um projeto Go e fazer coisas como enviar e receber moedas.
|
||||
|
||||
## O Que Vem Depois?
|
||||
|
||||
Vamos aprender mais sobre "Conversando com o Bitcoind com Outras Linguagens" na seção [§17.2: Acessando Bitcoin com Java](17_2_Accessing_Bitcoind_with_Java.md).
|
332
pt/17_2_Accessing_Bitcoind_with_Java.md
Normal file
332
pt/17_2_Accessing_Bitcoind_with_Java.md
Normal file
@ -0,0 +1,332 @@
|
||||
# 17.2: Acessando o Bitcoind com Java
|
||||
|
||||
> :information_source: **NOTA:** Esta seção foi adicionada recentemente ao curso e é um rascunho inicial que ainda pode estar aguardando revisão.
|
||||
|
||||
Esta seção explica como interagir com `bitcoind` usando a linguagem de programação Java e o [JavaBitcoindRpcClient](https://github.com/Polve/JavaBitcoindRpcClient).
|
||||
|
||||
## Configurando o Java
|
||||
|
||||
Podemos instalar o Java em nosso servidor, usando o comando `apt-get`. Também precisaremos instalar o [Apache Maven](http://maven.apache.org/) para gerenciar as dependências.
|
||||
```
|
||||
$ sudo apt-get install openjdk-11-jre-headless maven
|
||||
```
|
||||
|
||||
Podemos verificar a instalação do Java usando o comando abaixo:
|
||||
```
|
||||
$ java -version
|
||||
openjdk version "11.0.8" 2020-07-14
|
||||
OpenJDK Runtime Environment (build 11.0.8+10-post-Debian-1deb10u1)
|
||||
OpenJDK 64-Bit Server VM (build 11.0.8+10-post-Debian-1deb10u1, mixed mode, sharing)
|
||||
```
|
||||
|
||||
### Criando um projeto Maven
|
||||
|
||||
Para programar o Bitcoin usando o java, criaremos um projeto Maven:
|
||||
```
|
||||
$ mvn archetype:generate -DgroupId=com.blockchaincommons.lbtc -DartifactId=java-project -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
|
||||
```
|
||||
Isso irá baixar algumas dependências:
|
||||
```
|
||||
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom
|
||||
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom (4 KB at 4.2 KB/sec)
|
||||
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom
|
||||
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom (13 KB at 385.9 KB/sec)
|
||||
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/21/maven-parent-21.pom
|
||||
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/21/maven-parent-21.pom (26 KB at 559.6 KB/sec)
|
||||
Downloading: https://repo.maven.apache.org/maven2/org/apache/apache/10/apache-10.pom
|
||||
..............
|
||||
```
|
||||
Ele também criará um arquivo de configuração `pom.xml`:
|
||||
|
||||
```
|
||||
$ cd java-project
|
||||
$ ls -lagh
|
||||
total 16K
|
||||
drwxr-xr-x 3 sudo 4.0K Sep 1 13:58 .
|
||||
drwxr-xr-x 15 sudo 4.0K Sep 1 13:58 ..
|
||||
-rw-r--r-- 1 sudo 663 Sep 1 13:58 pom.xml
|
||||
drwxr-xr-x 4 sudo 4.0K Sep 1 13:58 src
|
||||
```
|
||||
|
||||
Para incluir o `JavaBitcoindRpcClient`, devemos adicionar nossa dependência a `<dependendencies>` no arquivo `pom.xml`.
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>wf.bitcoin</groupId>
|
||||
<artifactId>bitcoin-rpc-client</artifactId>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
```
|
||||
Também precisa adicionar propriedades do compilador para indicar qual versão do JDK compilará o código-fonte.
|
||||
|
||||
```
|
||||
<properties>
|
||||
<!-- https://maven.apache.org/general.html#encoding-warning -->
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
```
|
||||
|
||||
Sempre que adicionarmos um código-fonte às nossas aulas, poderemos testá-lo com:
|
||||
```
|
||||
$ mvn compile
|
||||
```
|
||||
Também podemos executá-lo com `exec: java`
|
||||
```
|
||||
$ mvn exec:java -Dexec.mainClass=com.blockchaincommons.lbtc.App
|
||||
```
|
||||
|
||||
### Criando Projetos Alternativos
|
||||
|
||||
Se usamos o [Gradle](https://gradle.org/releases/), podemos executar o seguinte comando:
|
||||
```groovy
|
||||
compile 'wf.bitcoin:JavaBitcoindRpcClient:1.2.1'
|
||||
```
|
||||
|
||||
Se quisermos um projeto de amostra e algumas instruções sobre como executá-lo no servidor que acabamos de criar, podemos consultar o [Projeto de amostra do Bitcoind Java](https://github.com/brunocvcunha/bitcoind-java-client- amostra/). Também pode navegar por todo o código de origem do [bitcoin-rpc-client](https://github.com/Polve/bitcoin-rpc-client).
|
||||
|
||||
## Construindo nossa conexão
|
||||
|
||||
Para usar o `JavaBitcoindRpcClient`, precisamos criar uma instância `BitcoindRpcClient`. Fazemos isso criando um URL com argumentos de nome de usuário, senha, endereço IP e porta. Como devemos nos lembrar, o endereço IP é `127.0.0.1` e a porta `18332` devem estar corretos para a configuração testnet padrão descrita neste curso, enquanto podemos extrair o usuário e a senha abrindo o arquivo `~/.bitcoin/bitcoin.conf`.
|
||||
|
||||
```java
|
||||
BitcoindRpcClient rpcClient = new BitcoinJSONRPCClient("http://StandUp:6305f1b2dbb3bc5a16cd0f4aac7e1eba@localhost:18332");
|
||||
```
|
||||
Podemos observar que também precisaremos importar as informações apropriadas:
|
||||
```
|
||||
import wf.bitcoin.javabitcoindrpcclient.BitcoinJSONRPCClient;
|
||||
import wf.bitcoin.javabitcoindrpcclient.BitcoindRpcClient;
|
||||
```
|
||||
|
||||
> **MAINNET VS TESTNET:** A porta seria 8332 para uma configuração da mainnet.
|
||||
|
||||
Se o `rpcClient` for inicializado com sucesso, podemos enviar comandos RPC.
|
||||
|
||||
Mais tarde, quando terminarmos nossa conexão `bitcoind`, precisamos fechá-la:
|
||||
```
|
||||
rpcClient.stop();
|
||||
```
|
||||
|
||||
### Fazendo uma chamada RPC
|
||||
|
||||
Você verá que o `BitcoindRpcClient` fornece a maioria das funcionalidades que podem ser acessadas através do `bitcoin-cli` ou outros métodos RPC, usando o mesmo método que o nome, mas em camelCase.
|
||||
|
||||
Por exemplo, para executar o comando `getmininginfo` para obter as informações do bloco e a dificuldade na rede, devemos usar o método `getMiningInfo()`:
|
||||
```java
|
||||
MiningInfo info = rpcClient.getMiningInfo();
|
||||
System.out.println("Mining Information");
|
||||
System.out.println("------------------");
|
||||
System.out.println("Chain......: " + info.chain());
|
||||
System.out.println("Blocks.....: " + info.blocks());
|
||||
System.out.println("Difficulty.: " + info.difficulty());
|
||||
System.out.println("Hash Power.: " + info.networkHashps());
|
||||
```
|
||||
O retorno para esta linha deve algo próximo a isso:
|
||||
```
|
||||
Mining Information
|
||||
------------------
|
||||
Chain......: test
|
||||
Blocks.....: 1830905
|
||||
Difficulty.: 4194304
|
||||
Hash Power.: 40367401348837.41
|
||||
```
|
||||
### Fazendo uma chamada RPC usando argumentos
|
||||
|
||||
Podemos procurar os endereços em nossa carteira passando o endereço como um argumento para o comando `getAddressInfo`:
|
||||
|
||||
```java
|
||||
String addr1 = "mvLyH7Rs45c16FG2dfV7uuTKV6pL92kWxo";
|
||||
|
||||
AddressInfo addr1Info = rpcClient.getAddressInfo(addr1);
|
||||
System.out.println("Address: " + addr1Info.address());
|
||||
System.out.println("MasterFingerPrint: " + addr1Info.hdMasterFingerprint());
|
||||
System.out.println("HdKeyPath: " + addr1Info.hdKeyPath());
|
||||
System.out.println("PubKey: " + addr1Info.pubKey());
|
||||
```
|
||||
O resultado será mais ou menos assim:
|
||||
```
|
||||
Address: mvLyH7Rs45c16FG2dfV7uuTKV6pL92kWxo
|
||||
MasterFingerPrint: ce0c7e14
|
||||
HdKeyPath: m/0'/0'/5'
|
||||
PubKey: 0368d0fffa651783524f8b934d24d03b32bf8ff2c0808943a556b3d74b2e5c7d65
|
||||
```
|
||||
|
||||
### Executando nosso código
|
||||
|
||||
O código para esses exemplos pode ser encontrado no [diretório src/](src/17_2_App-getinfo.java) e deve ser instalado na estrutura de diretório padrão criada neste caso como `~/java-project/src/main/java/com/blockchaincommons/lbtc/App.java`. Ele pode então ser compilado e executado.
|
||||
|
||||
```
|
||||
$ mvn compile
|
||||
$ mvn exec:java -Dexec.mainClass=com.blockchaincommons.lbtc.App
|
||||
Chain......: test
|
||||
Blocks.....: 1831079
|
||||
Difficulty.: 4194304
|
||||
Hash Power.: 38112849943221.16
|
||||
Address: mvLyH7Rs45c16FG2dfV7uuTKV6pL92kWxo
|
||||
MasterFingerPrint: ce0c7e14
|
||||
HdKeyPath: m/0'/0'/5'
|
||||
PubKey: 0368d0fffa651783524f8b934d24d03b32bf8ff2c0808943a556b3d74b2e5c7d65
|
||||
```
|
||||
Você também verá muito mais informações sobre a compilação, é claro.
|
||||
|
||||
## Pesquisando os fundos
|
||||
|
||||
Recuperando o saldo de uma conta inteira é tão fácil quanto:
|
||||
```
|
||||
System.out.println("Balance: " + rpcClient.getBalance());
|
||||
```
|
||||
|
||||
## Criando um endereço
|
||||
|
||||
Podemos criar um novo endereço em nossa carteira, anexando um rótulo específico a ela e até mesmo descartando nossa chave privada.
|
||||
|
||||
```java
|
||||
String address = rpcClient.getNewAddress("Learning-Bitcoin-from-the-Command-Line");
|
||||
System.out.println("New Address: " + address);
|
||||
|
||||
String privKey = rpcClient.dumpPrivKey(address);
|
||||
System.out.println("Priv Key: " + privKey);
|
||||
```
|
||||
Retorno:
|
||||
```
|
||||
New Address: mpsFtZ8qTJPRGZy1gaaUw37fHeUSPLkzzs
|
||||
Priv Key: cTy2AnmAALsHokYzJzTdsUBSqBtypmWfmSNYgG6qQH43euUZgqic
|
||||
```
|
||||
|
||||
## Enviando uma transação
|
||||
|
||||
A biblioteca `JavaBitcoindRpcClient` possui algumas ferramentas interessantes que facilitam a criação de uma transação desde o início.
|
||||
|
||||
### Criando uma transação
|
||||
|
||||
Podemos criar uma transação bruta usando o método `createRawTransaction`, passando como argumentos dois objetos ArrayList contendo entradas e saídas a serem utilizadas.
|
||||
|
||||
Primeiro configuramos nossos novos endereços, aqui um endereço existente em nosso sistema e um novo endereço.
|
||||
```
|
||||
String addr1 = "tb1qdqkc3430rexxlgnma6p7clly33s6jjgay5q8np";
|
||||
System.out.println("Used address addr1: " + addr1);
|
||||
|
||||
String addr2 = rpcClient.getNewAddress();
|
||||
System.out.println("Created address addr2: " + addr2);
|
||||
```
|
||||
Então, podemos usar o RPC `listUnspent` para encontrar UTXOs para o endereço existente.
|
||||
```
|
||||
List<Unspent> utxos = rpcClient.listUnspent(0, Integer.MAX_VALUE, addr1);
|
||||
System.out.println("Found " + utxos.size() + " UTXOs (unspent transaction outputs) belonging to addr1");
|
||||
```
|
||||
Aqui está um resultado de todas as informações:
|
||||
```java
|
||||
System.out.println("Created address addr1: " + addr1);
|
||||
String addr2 = rpcClient.getNewAddress();
|
||||
System.out.println("Created address addr2: " + addr2);
|
||||
List<String> generatedBlocksHashes = rpcClient.generateToAddress(110, addr1);
|
||||
System.out.println("Generated " + generatedBlocksHashes.size() + " blocks for addr1");
|
||||
List<Unspent> utxos = rpcClient.listUnspent(0, Integer.MAX_VALUE, addr1);
|
||||
System.out.println("Found " + utxos.size() + " UTXOs (unspent transaction outputs) belonging to addr1");
|
||||
```
|
||||
As transações são criadas com o comando `BitcoinRawTxBuilder`:
|
||||
```
|
||||
BitcoinRawTxBuilder txb = new BitcoinRawTxBuilder(rpcClient);
|
||||
```
|
||||
Primeiro preenchemos as entradas com os UTXOs que estamos gastando:
|
||||
```
|
||||
TxInput in = utxos.get(0);
|
||||
txb.in(in);
|
||||
```
|
||||
|
||||
> :warning: **AVISO:** Obviamente, em um programa real, selecionaríamos um UTXO de forma inteligente, porém, neste caso, pegamos apenas o enésimo, uma tática que usaremos ao longo deste capítulo.
|
||||
|
||||
Em segundo lugar, preenchemos as saídas, cada uma com um valor e um endereço:
|
||||
```
|
||||
BigDecimal estimatedFee = BigDecimal.valueOf(0.00000200);
|
||||
BigDecimal txToAddr2Amount = utxos.get(0).amount().subtract(estimatedFee);
|
||||
txb.out(addr2, txToAddr2Amount);
|
||||
|
||||
System.out.println("unsignedRawTx in amount: " + utxos.get(0).amount());
|
||||
System.out.println("unsignedRawTx out amount: " + txToAddr2Amount);
|
||||
```
|
||||
|
||||
Agora estamos prontos para realmente criar a transação:
|
||||
```
|
||||
String unsignedRawTxHex = txb.create();
|
||||
System.out.println("Created unsignedRawTx from addr1 to addr2: " + unsignedRawTxHex);
|
||||
```
|
||||
|
||||
### Assinando uma transação
|
||||
|
||||
Agora podemos assinar a transação com o método `signRawTransactionWithKey`. Este método recebe como parâmetros uma transação de string bruta não assinada, a chave privada do endereço de envio e o objeto TxInput.
|
||||
|
||||
```java
|
||||
SignedRawTransaction srTx = rpcClient.signRawTransactionWithKey(
|
||||
unsignedRawTxHex,
|
||||
Arrays.asList(rpcClient.dumpPrivKey(addr1)), //
|
||||
Arrays.asList(in),
|
||||
null);
|
||||
System.out.println("signedRawTx hex: " + srTx.hex());
|
||||
System.out.println("signedRawTx complete: " + srTx.complete());
|
||||
```
|
||||
|
||||
### Enviando uma transação
|
||||
|
||||
Finalmente, o envio requer o comando `sendRawTransaction`:
|
||||
```java
|
||||
String sentRawTransactionID = rpcClient.sendRawTransaction(srTx.hex());
|
||||
System.out.println("Sent signedRawTx (txID): " + sentRawTransactionID);
|
||||
```
|
||||
|
||||
### Executando nosso código
|
||||
|
||||
Agora podemos executar [o código da transação](src/17_2_App-sendtx.java) como `~/java-project/src/main/java/com/blockchaincommons/lbtc/App.java`.
|
||||
|
||||
```
|
||||
$ mvn compile
|
||||
$ mvn exec:java -Dexec.mainClass=com.blockchaincommons.lbtc.App
|
||||
Used address addr1: tb1qdqkc3430rexxlgnma6p7clly33s6jjgay5q8np
|
||||
Created address addr2: tb1q04q2wzlhfqlrnz95ynfj7gp4t3yynrj0542smv
|
||||
Found 1 UTXOs (unspent transaction outputs) belonging to addr1
|
||||
unsignedRawTx in amount: 0.00850000
|
||||
unsignedRawTx out amount: 0.00849800
|
||||
Created unsignedRawTx from addr1 to addr2: 0200000001d2a90fc3b43e8eb4ae9452af43c9448112d359cac701f7f537aa8b6f39193bb90100000000ffffffff0188f70c00000000001600147d40a70bf7483e3988b424d32f20355c48498e4f00000000
|
||||
signedRawTx hex: 02000000000101d2a90fc3b43e8eb4ae9452af43c9448112d359cac701f7f537aa8b6f39193bb90100000000ffffffff0188f70c00000000001600147d40a70bf7483e3988b424d32f20355c48498e4f024730440220495fb64d8cf9dee9daa8535b8867709ac8d3763d693fd8c9111ce610645c76c90220286f39a626a940c3d9f8614524d67dd6594d9ee93818927df4698c1c8b8f622d01210333877967ac52c0d0ec96aca446ceb3f51863de906e702584cc4da2780d360aae00000000
|
||||
signedRawTx complete: true
|
||||
Sent signedRawTx (txID): 82032c07e0ed91780c3369a1943ea8abf49c9e11855ffedd935374ecbc789c45
|
||||
```
|
||||
|
||||
## Ouvindo as transações ou blocos
|
||||
|
||||
Tal como acontece com [C e nas nossas bibliotecas ZMQ](15_3_Receiving_Bitcoind_Notifications_with_C.md), existem maneiras fáceis de usar o Java para ouvir a blockchain, além de executar o código específico quando algo acontece, como uma transação que envolve um endereço em nossa carteira, ou até a geração de um novo bloco na rede.
|
||||
|
||||
Para fazer isso, podemos usar a classe `BitcoinAcceptor` do `JavaBitcoindRpcClient`, que permite anexar _listerners_ na rede.
|
||||
|
||||
```java
|
||||
String blockHash = rpcClient.getBestBlockHash();
|
||||
BitcoinAcceptor acceptor = new BitcoinAcceptor(rpcClient, blockHash, 6, new BitcoinPaymentListener() {
|
||||
|
||||
@Override
|
||||
public void transaction(Transaction tx) {
|
||||
System.out.println("Transaction: " + tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void block(String block) {
|
||||
System.out.println("Block: " + block);
|
||||
}
|
||||
});
|
||||
acceptor.run();
|
||||
```
|
||||
|
||||
Veja [o diretório src/](src / 17_2_App-listen.java) para o código completo. Cada vez que uma transação é enviada ou um novo bloco é gerado, devemos ver a saída em nosso console:
|
||||
```
|
||||
Transaction: {account=Tests, address=mhopuJzgmTwhGfpNLCJ9CRknugY691oXp1, category=receive, amount=5.0E-4, label=Tests, vout=1, confirmations=0, trusted=false, txid=361e8fcff243b74ebf396e595a007636654f67c3c7b55fd2860a3d37772155eb, walletconflicts=[], time=1513132887, timereceived=1513132887, bip125-replaceable=unknown}
|
||||
|
||||
Block: 000000004564adfee3738314549f7ca35d96c4da0afc6b232183917086b6d971
|
||||
```
|
||||
|
||||
### Resumo: Acessando o Bitcoind com Java
|
||||
|
||||
Usando a biblioteca `javabitcoinrpc`, podemos acessar facilmente o `bitcoind` por meio de chamadas RPC no Java. Também teremos acesso a recursos adicionais interessantes, como o serviço de escuta usando o comando `bitcoinAcceptor`.
|
||||
|
||||
## O Que Vem Depois?
|
||||
|
||||
Vamos aprender mais sobre "Conversando com o Bitcoind com Outras Linguagens" na seção [§17.3: Acessando o Bitcoind com NodeJS](17_2_Accessing_Bitcoind_with_Java.md).
|
274
pt/17_3_Accessing_Bitcoind_with_NodeJS.md
Normal file
274
pt/17_3_Accessing_Bitcoind_with_NodeJS.md
Normal file
@ -0,0 +1,274 @@
|
||||
# 17.3: Acessando o Bitcoind com NodeJS
|
||||
|
||||
> :information_source: **NOTA:** Esta seção foi adicionada recentemente ao curso e é um rascunho inicial que ainda pode estar aguardando revisão.
|
||||
|
||||
Esta seção explica como interagir com o `bitcoind` usando a linguagem de programação NodeJS e o [pacote BCRPC](https://github.com/dgarage/bcrpc).
|
||||
|
||||
## Set Up Node.js
|
||||
## Configurando o Node.js
|
||||
|
||||
O BCRPC é construído em node.js. Portanto, primeiro precisamos instalar os pacotes `node.js` e o `npm` (o gerenciador de pacotes do node) em nosso sistema.
|
||||
|
||||
Se estiver usando uma máquina Ubuntu, podemos executar os seguintes comandos para obter uma nova versão do `node.js`, ao invés da versão terrivelmente desatualizada no sistema de pacotes do Ubuntu.
|
||||
|
||||
```
|
||||
$ curl -sL https://deb.nodesource.com/setup_14.x | sudo bash -
|
||||
$ sudo apt-get install -y nodejs
|
||||
$ sudo npm install mocha -g
|
||||
```
|
||||
|
||||
### Configurando o BCRPC
|
||||
|
||||
Agora podemos clonar o pacote BCRPC do GitHub e instalar as dependências.
|
||||
|
||||
```
|
||||
$ git clone https://github.com/dgarage/bcrpc.git
|
||||
$ cd bcrpc
|
||||
$ npm install
|
||||
```
|
||||
|
||||
Para testar o pacote BCRPC, devemos primeiro definir as variáveis ambientais para o rpcuser e rpcpassword. Como de costume, eles vêm do `~/.bitcoin/bitcoin.conf`. Também devemos definir a porta RPC como sendo 18332, que deve ser a correta para a configuração Testnet padrão descrita nos documentos.
|
||||
|
||||
```
|
||||
$ export BITCOIND_USER=StandUp
|
||||
$ export BITCOIND_PASS=d8340efbcd34e312044c8431c59c792c
|
||||
$ export BITCOIND_PORT=18332
|
||||
```
|
||||
|
||||
> :warning: **AVISO:** Obviamente, nunca colocaria nossa senha definida em uma variável de ambiente em um ambiente de produção.
|
||||
|
||||
> :link: **MAINNET VS TESTNET:** A porta seria 8332 para uma configuração na Mainnet.
|
||||
|
||||
Agora podemos verificar se tudo está funcionando corretamente:
|
||||
|
||||
```
|
||||
$ npm test
|
||||
|
||||
> bcrpc@0.2.2 test /home/user1/bcrpc
|
||||
> mocha tests.js
|
||||
|
||||
BitcoinD
|
||||
✓ is running
|
||||
|
||||
bcrpc
|
||||
✓ can get info
|
||||
|
||||
2 passing (36ms)
|
||||
```
|
||||
Parabéns, agora temos um wrapper RPC pronto para usar o Bitcoin com o Node.js que está funcionando com nossa configuração do Bitcoin.
|
||||
|
||||
### Criando um Projeto BCRPC
|
||||
|
||||
Agora podemos criar um novo projeto Node.js e instalar o BCRPC via npm.
|
||||
|
||||
```
|
||||
$ cd ~
|
||||
$ mkdir myproject
|
||||
$ cd myproject
|
||||
$ npm init
|
||||
[continue with default options]
|
||||
$ npm install bcrpc
|
||||
```
|
||||
## Construindo nossa conexão
|
||||
|
||||
Em nosso diretório `myproject`, criamos um arquivo `.js` onde nosso código JavaScript será executado.
|
||||
|
||||
Podemos iniciar uma conexão RPC criando um `RpcAgent`:
|
||||
```
|
||||
const RpcAgent = require('bcrpc');
|
||||
agent = new RpcAgent({port: 18332, user: 'StandUp', pass: 'd8340efbcd34e312044c8431c59c792c'});
|
||||
```
|
||||
Obviamente, nosso `user` e `pass` devem coincidir novamente com o que está em nosso `~/.bitcoin/bitcoin.conf`, e usamos a `porta 18332` se estivermos na Testnet.
|
||||
|
||||
### Fazendo uma chamada RPC
|
||||
|
||||
Usando o BCRPC, podemos usar os mesmos comandos RPC que normalmente usaríamos via `bitcoin-cli` com nosso `RpcAgent`, exceto que eles precisam estar em camelCase. Por exemplo, `getblockhash` seria `getBlockHash`.
|
||||
|
||||
Para imprimir o número do bloco mais recente, basta chamar o `getBlockCount` através do nosso `RpcAgent`:
|
||||
|
||||
```
|
||||
agent.getBlockCount(function (err, blockCount) {
|
||||
if (err)
|
||||
throw Error(JSON.stringify(err));
|
||||
console.log(blockCount.result);
|
||||
});
|
||||
```
|
||||
|
||||
### Fazendo uma chamada RPC com argumentos
|
||||
|
||||
As funções BCRPC podem aceitar argumentos. Por exemplo, o `getBlockHash` recebe o `blockCount.result` como uma entrada.
|
||||
|
||||
```
|
||||
agent.getBlockHash(blockCount.result, function (err, hash) {
|
||||
if (err)
|
||||
throw Error(JSON.stringify(err));
|
||||
console.log(hash.result);
|
||||
})
|
||||
```
|
||||
|
||||
O resultado das funções BCRPC é um objeto JSON contendo informações sobre quaisquer erros e o id da solicitação. Ao acessar nosso resultado, adicionamos o `.result` no final dele para especificar que estamos interessados no resultado real, não em informações sobre os erros.
|
||||
|
||||
### Executando nosso código
|
||||
|
||||
Podemos encontrar o código `getinfo` no [diretório src/](src / 17_3_getinfo.js).
|
||||
```
|
||||
$ node getinfo.js
|
||||
1831094
|
||||
00000000000002bf8b522a830180ad3a93b8eed33121f54b3842d8838580a53c
|
||||
```
|
||||
Isto é o que a saída do exemplo acima pareceria se substituíssemos o `console.log(blockCount.result);` e o `console.log(hash.result);` por `console.log(blockCount);` e `console.log (hash);`, respectivamente:
|
||||
|
||||
```
|
||||
{ result: 1774686, error: null, id: null }
|
||||
{
|
||||
result: '00000000000000d980c495a2b7addf09bb0a9c78b5b199c8e965ee54753fa5da',
|
||||
error: null,
|
||||
id: null
|
||||
}
|
||||
```
|
||||
|
||||
## Pesquisando os fundos
|
||||
|
||||
É útil ao aceitar Bitcoin verificar os Bitcoin recebidos em um endereço específico em nossa carteira. Por exemplo, se administrássemos uma loja online que aceita Bitcoin, para cada pagamento de um cliente, geraríamos um novo endereço, mostraríamos esse endereço ao cliente e, em seguida, verificaríamos o saldo do endereço após algum tempo, para certificar-se de que o montante foi recebido:
|
||||
|
||||
```
|
||||
agent.getReceivedByAddress('mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha', function (err, addressInfo) {
|
||||
if (err)
|
||||
throw Error(JSON.stringify(err));
|
||||
console.log(addressInfo.result);
|
||||
});
|
||||
```
|
||||
|
||||
> :information_source: **NOTA:** Obviamente, precisaremos inserir um endereço reconhecido por nossa máquina.
|
||||
|
||||
Por padrão, esta função verifica as transações que foram confirmadas uma vez, no entanto, podemos aumentar para um número maior, como 6:
|
||||
|
||||
```
|
||||
agent.getReceivedByAddress('mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha', 6, function (err, addressInfo) {
|
||||
if (err)
|
||||
throw Error(JSON.stringify(err));
|
||||
console.log(addressInfo.result);
|
||||
});
|
||||
```
|
||||
|
||||
### Pesquisando informações da carteira
|
||||
|
||||
Também podemos procurar informações adicionais sobre nossa carteira e visualizar nosso saldo, contagem de transações etc.
|
||||
|
||||
```
|
||||
agent.getWalletInfo(function (err, walletInfo) {
|
||||
if (err)
|
||||
throw Error(JSON.stringify(err));
|
||||
console.log(walletInfo.result);
|
||||
});
|
||||
```
|
||||
|
||||
O código está disponível como [walletinfo.js](src/17_3_walletinfo.js).
|
||||
```
|
||||
$ node walletinfo.js
|
||||
0.008498
|
||||
{
|
||||
walletname: '',
|
||||
walletversion: 169900,
|
||||
balance: 0.010438,
|
||||
unconfirmed_balance: 0,
|
||||
immature_balance: 0,
|
||||
txcount: 4,
|
||||
keypoololdest: 1596567843,
|
||||
keypoolsize: 999,
|
||||
hdseedid: 'da5a1b058deb9e51ecffef1b0ddc069a5dfb2c5f',
|
||||
keypoolsize_hd_internal: 1000,
|
||||
paytxfee: 0,
|
||||
private_keys_enabled: true,
|
||||
avoid_reuse: false,
|
||||
scanning: false
|
||||
}
|
||||
```
|
||||
Ao invés de imprimir todos os detalhes associados à nossa carteira, podemos imprimir informações específicas, como nosso saldo. Como um objeto JSON está sendo acessado, podemos fazer isso alterando a linha `console.log(walletInfo.result);` para `console.log(walletInfo.result.balance);`:
|
||||
|
||||
## Criando um endereço
|
||||
|
||||
Também podemos passar argumentos adicionais para os comandos RPC. Por exemplo, o seguinte gera um novo endereço legado, com o sinalizador `-addresstype`.
|
||||
|
||||
```
|
||||
agent.getNewAddress('-addresstype', 'legacy', function (err, newAddress) {
|
||||
if (err)
|
||||
throw Error(JSON.stringify(err));
|
||||
console.log(newAddress.result);
|
||||
});
|
||||
```
|
||||
Isso é o mesmo que executar o seguinte na linha de comando:
|
||||
```
|
||||
$ bitcoin-cli getnewaddress -addresstype legacy
|
||||
mtGPcBvRPZFEHo2YX8un9qqPBydhG82uuZ
|
||||
```
|
||||
|
||||
No BCRPC, geralmente podemos usar os mesmos sinalizadores que no `bitcoin-cli` no BCRPC. Embora usamos o camelCase (`getNewAddress`) para os métodos, os sinalizadores, que normalmente são separados por espaços na linha de comando, são colocados em strings e separados por vírgulas.
|
||||
|
||||
## Enviando uma transação
|
||||
|
||||
Podemos enviar saldos para um endereço muito facilmente, usando a função `sendToAddress`:
|
||||
|
||||
```
|
||||
agent.sendToAddress(newAddress.result, 0.00001, function(err, txid) {
|
||||
if (err)
|
||||
throw Error(JSON.stringify(err));
|
||||
console.log(txid.result);
|
||||
});
|
||||
```
|
||||
|
||||
Isso deve retornar o txid da transação:
|
||||
|
||||
```
|
||||
1679bee019c61608340b79810377be2798efd4d2ec3ace0f00a1967af70666b9
|
||||
```
|
||||
|
||||
### Pesquisando uma transação
|
||||
|
||||
Agora podemos desejar visualizar uma transação, como a que acabamos de enviar.
|
||||
```
|
||||
agent.getTransaction(txid.result, function (err, transaction) {
|
||||
if (err)
|
||||
throw Error(JSON.stringify(err));
|
||||
console.log(transaction.result);
|
||||
});
|
||||
```
|
||||
|
||||
Devemos obter uma saída semelhante a esta:
|
||||
|
||||
```
|
||||
{
|
||||
amount: 0.001,
|
||||
confirmations: 4776,
|
||||
blockhash: '000000006628870b0a8a66abea9cf0d4e815c491f079e3fa9e658a87b5dc863a',
|
||||
blockindex: 117,
|
||||
blocktime: 1591857418,
|
||||
txid: '1661ce322c128e053b8ea8fcc22d17df680d2052983980e2281d692b9b4ab7df',
|
||||
walletconflicts: [],
|
||||
time: 1591857343,
|
||||
timereceived: 1591857343,
|
||||
'bip125-replaceable': 'no',
|
||||
details: [
|
||||
{
|
||||
address: 'mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha',
|
||||
category: 'receive',
|
||||
amount: 0.001,
|
||||
label: '',
|
||||
vout: 0
|
||||
}
|
||||
],
|
||||
hex: '02000000000101e9e8c3bd057d54e73baadc60c166860163b0e7aa60cab33a03e89fb44321f8d5010000001716001435c2aa3fc09ea53c3e23925c5b2e93b9119b2568feffffff02a0860100000000001976a914600c8c6a4abb0a502ea4de01681fe4fa1ca7800688ac65ec1c000000000017a91425b920efb2fde1a0277d3df11d0fd7249e17cf8587024730440220403a863d312946aae3f3ef0a57206197bc67f71536fb5f4b9ca71a7e226b6dc50220329646cf786cfef79d60de3ef54f702ab1073694022f0618731902d926918c3e012103e6feac9d7a8ad1ac6b36fb4c91c1c9f7fff1e7f63f0340e5253a0e4478b7b13f41fd1a00'
|
||||
}
|
||||
```
|
||||
|
||||
O código completo está disponível no [sendtx.js](src/17_3_sendtx.js).
|
||||
|
||||
## Resumo: Acessando o Bitcoind com NodeJS
|
||||
|
||||
Com o BCRPC podemos acessar todos os comandos RPC disponíveis através do `bitcoin-cli`, usando o JavaScript. O [BCRPC README](https://github.com/dgarage/bcrpc) tem alguns exemplos que usam promises (os exemplos neste livro usam callbacks). O [JavaScript por trás dele](https://github.com/dgarage/bcrpc/blob/master/index.js) é curto e bem legível.
|
||||
|
||||
Com base nesses exemplos, devemos ser capazes de incorporar Bitcoin em um projeto Node.js e fazer coisas como enviar e receber fundos.
|
||||
|
||||
## O Que Vem Depois?
|
||||
|
||||
Vamos aprender mais sobre "Conversando com o Bitcoind com Outras Linguagens" na seção [§17.4: Acessando o Bitcoind com Python](17_2_Accessing_Bitcoind_with_Java.md).
|
495
pt/17_4_Accessing_Bitcoind_with_Python.md
Normal file
495
pt/17_4_Accessing_Bitcoind_with_Python.md
Normal file
@ -0,0 +1,495 @@
|
||||
# 17.4: Acessando o Bitcoind com Python
|
||||
|
||||
> :information_source: **NOTA:** Esta seção foi adicionada recentemente ao curso e é um rascunho inicial que ainda pode estar aguardando revisão.
|
||||
|
||||
Esta seção explica como interagir com o `bitcoind` usando a linguagem de programação Python e o [Python-BitcoinRPC](https://github.com/jgarzik/python-bitcoinrpc).
|
||||
|
||||
## Configurando o Python
|
||||
|
||||
Se já temos o Bitcoin Core instalado, provavelmente temos o Python 3 disponível.
|
||||
Podemos verificar isso executando:
|
||||
|
||||
```$ python3 --version```
|
||||
|
||||
Se ele retornar um número de versão (por exemplo, `3.7.3` ou `3.8.3`), então temos o python3 instalado.
|
||||
|
||||
No entanto, se não tivermos o Python instalado, precisaremos compilá-lo a partir do código-fonte. Podemos consultar como fazer isso, como mostrado no ["Construindo Python a partir da fonte"](17_4_Accessing_Bitcoind_with_Python.md # variant-build-python-from-source) antes de continuarmos.
|
||||
|
||||
### Configurando o BitcoinRPC
|
||||
|
||||
Quer tenhamos usado um Python existente ou compilado a partir da fonte, agora estamos prontos para instalar a biblioteca `python-bitcoinrpc`:
|
||||
|
||||
```
|
||||
$ pip3 install python-bitcoinrpc
|
||||
```
|
||||
Se você não instalamos o `pip`, precisaremos executar o seguinte:
|
||||
```
|
||||
$ sudo apt install python3-pip
|
||||
```
|
||||
Em seguida, vamos repetir o comando `pip3 install python-bitcoinrpc`.
|
||||
|
||||
### Criar um projeto BitcoinRPC
|
||||
|
||||
Geralmente, precisaremos incluir declarações apropriadas do `bitcoinrpc` em nosso projetos Bitcoin usando o Python. O seguinte fornecerá acesso aos comandos baseados em RPC:
|
||||
```py
|
||||
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
|
||||
```
|
||||
Também podemos achar o seguinte comando útil:
|
||||
```py
|
||||
from pprint import pprint
|
||||
import logging
|
||||
```
|
||||
O `pprint` irá imprimir a resposta `json` do `bitcoind`.
|
||||
|
||||
O `logging` irá imprimir a chamada que fizemos para o respose do `bitcoind` e o próprio `bitcoind`, o que é útil quando fazemos um monte de chamadas juntas. Se não quisermos um retorno muito grande no terminal, podemos comentar o bloco `logging`.
|
||||
|
||||
## Construindo a nossa conexão
|
||||
|
||||
Agora estamos prontos para começar a interagir com o `bitcoind` estabelecendo uma conexão. Vamos criar um arquivo chamado `btcrpc.py` e digitar o seguinte:
|
||||
|
||||
```py
|
||||
logging.basicConfig()
|
||||
logging.getLogger("BitcoinRPC").setLevel(logging.DEBUG)
|
||||
# rpc_user and rpc_password are set in the bitcoin.conf file
|
||||
rpc_user = "StandUp"
|
||||
rpc_pass = "6305f1b2dbb3bc5a16cd0f4aac7e1eba"
|
||||
rpc_host = "127.0.0.1"
|
||||
rpc_client = AuthServiceProxy(f"http://{rpc_user}:{rpc_pass}@{rpc_host}:18332", timeout=120)
|
||||
```
|
||||
|
||||
Os argumentos na URL são `<rpc_username>:<rpc_password>@<host_IP_address>:<port>`. Como de costume, o `user` e o `pass` são encontrados no arquivo `~/.bitcoin/bitcoin.conf`, enquanto o `host` é o nosso localhost, e a porta é `18332` para Testnet. O argumento `timeout` é especificado desde o tempo limite dos sockets sob grande demanda na rede principal. Se obtivermos a resposta `socket.timeout: timed out`, precisamos ser pacientes e aumentar o` timeout`.
|
||||
|
||||
> :link: ** MAINNET VS TESTNET: ** A porta seria 8332 para uma configuração na Mainnet.
|
||||
|
||||
### Fazendo uma chamada RPC
|
||||
|
||||
Se o `rpc_client` for inicializado com sucesso, seremos capazes de enviar comandos RPC para o nosso node do Bitcoin.
|
||||
|
||||
Para usar um método RPC do `python-bitcoinrpc`, usaremos o objeto `rpc_client` que criamos, que fornece a maior parte da funcionalidade que pode ser acessada através do `bitcoin-cli`, usando os mesmos nomes do método.
|
||||
|
||||
Por exemplo, o comando a seguir irá recuperar a contagem de blocos do nosso node:
|
||||
|
||||
```py
|
||||
block_count = rpc_client.getblockcount()
|
||||
print("---------------------------------------------------------------")
|
||||
print("Block Count:", block_count)
|
||||
print("---------------------------------------------------------------\n")
|
||||
```
|
||||
|
||||
Devemos obter a seguinte resposta com o `logging` habilitado:
|
||||
|
||||
|
||||
```sh
|
||||
DEBUG:BitcoinRPC:-3-> getblockcount []
|
||||
DEBUG:BitcoinRPC:<-3- 1773020
|
||||
---------------------------------------------------------------
|
||||
Block Count: 1773020
|
||||
---------------------------------------------------------------
|
||||
```
|
||||
|
||||
### Fazendo uma chamada RPC enviando argumentos
|
||||
|
||||
Podemos usar esse blockcount como um argumento para recuperar o blockhash de um bloco e também para recuperar os detalhes do bloco.
|
||||
|
||||
Isso é feito enviando nossos comandos do objeto `rpc_client` com um argumento:
|
||||
```py
|
||||
blockhash = rpc_client.getblockhash(block_count)
|
||||
block = rpc_client.getblock(blockhash)
|
||||
```
|
||||
|
||||
O `getblockhash` retornará um único valor, enquanto o `getblock` retornará um array associativo de informações sobre o bloco, que inclui um array em `block['tx']` fornecendo detalhes sobre cada transação dentro do bloco:
|
||||
|
||||
```py
|
||||
nTx = block['nTx']
|
||||
if nTx > 10:
|
||||
it_txs = 10
|
||||
list_tx_heading = "First 10 transactions: "
|
||||
else:
|
||||
it_txs = nTx
|
||||
list_tx_heading = f"All the {it_txs} transactions: "
|
||||
print("---------------------------------------------------------------")
|
||||
print("BLOCK: ", block_count)
|
||||
print("-------------")
|
||||
print("Block Hash...: ", blockhash)
|
||||
print("Merkle Root..: ", block['merkleroot'])
|
||||
print("Block Size...: ", block['size'])
|
||||
print("Block Weight.: ", block['weight'])
|
||||
print("Nonce........: ", block['nonce'])
|
||||
print("Difficulty...: ", block['difficulty'])
|
||||
print("Number of Tx.: ", nTx)
|
||||
print(list_tx_heading)
|
||||
print("---------------------")
|
||||
i = 0
|
||||
while i < it_txs:
|
||||
print(i, ":", block['tx'][i])
|
||||
i += 1
|
||||
print("---------------------------------------------------------------\n")
|
||||
```
|
||||
|
||||
### Executando nosso código
|
||||
|
||||
Podemos usar [o código que está no src/](src/17_4_getinfo.py) e executá-lo com `python3`:
|
||||
|
||||
```
|
||||
$ python3 getinfo.py
|
||||
---------------------------------------------------------------
|
||||
Block Count: 1831106
|
||||
---------------------------------------------------------------
|
||||
---------------------------------------------------------------
|
||||
BLOCK: 1831106
|
||||
-------------
|
||||
Block Hash...: 00000000000003b2ea7c2cdfffd86156ad1f5606ab58e128940a2534d1348b04
|
||||
Merkle Root..: 056a547fe59208167eef86fa694263728fb684119254b340c1f86bdd423a8082
|
||||
Block Size...: 52079
|
||||
Block Weight.: 128594
|
||||
Nonce........: 1775583700
|
||||
Difficulty...: 4194304
|
||||
Number of Tx.: 155
|
||||
First 10 transactions:
|
||||
---------------------
|
||||
0 : d228d55112e3aa26265b0118cfdc98345c229d20fe074b9afb87107c03ce11b5
|
||||
1 : 92822e8e34fafb472b87c99ea3f3e16440452b3f361ed86c6fa62175173fb750
|
||||
2 : fa7c67600c14d4aa350a9674688f1429577954f4a6c5e4639d06c8964824f647
|
||||
3 : 3a91d1527e308e5603dafde7ab17824f441a73a779d2571d073466dc9e8451b2
|
||||
4 : 30fd0e5527b1522e7b26a4818b9edac80fe47c0c39fc34705478a49e684708d0
|
||||
5 : 24c5372b38c78cbaf5b0b305925502a491bc0c1b5758f50c0bd335abb6ae85f5
|
||||
6 : be70e125a5793efc5e32051fecba0668df971bdf371138c8261201c2a46b2d38
|
||||
7 : 41ebf52c847a59ba0aeb4425c74e89a01e91defa86a82785ff53ed4668054561
|
||||
8 : dc8211b4ce122f87692e7c203672e3eb1ffc44c0a307eafcc560323fcc5fae78
|
||||
9 : 59e2d8e11cad287eacf3207e64a373f65059286b803ef0981510193ae29cbc8c
|
||||
---------------------------------------------------------------
|
||||
```
|
||||
|
||||
## Pesquisando fundos
|
||||
|
||||
Da mesma forma, podemos recuperar as informações da nossa carteira com o RPC `getwalletinfo`:
|
||||
|
||||
```py
|
||||
wallet_info = rpc_client.getwalletinfo()
|
||||
print("---------------------------------------------------------------")
|
||||
print("Wallet Info:")
|
||||
print("-----------")
|
||||
pprint(wallet_info)
|
||||
print("---------------------------------------------------------------\n")
|
||||
```
|
||||
|
||||
Devemos obter um retorno semelhante ao que tivemos abaixo com o `logging` desabilitado:
|
||||
|
||||
```sh
|
||||
---------------------------------------------------------------
|
||||
Wallet Info:
|
||||
-----------
|
||||
{'avoid_reuse': False,
|
||||
'balance': Decimal('0.07160443'),
|
||||
'hdseedid': '6dko666b1cc0d69b7eb0539l89eba7b6390kdj02',
|
||||
'immature_balance': Decimal('0E-8'),
|
||||
'keypoololdest': 1542245729,
|
||||
'keypoolsize': 999,
|
||||
'keypoolsize_hd_internal': 1000,
|
||||
'paytxfee': Decimal('0E-8'),
|
||||
'private_keys_enabled': True,
|
||||
'scanning': False,
|
||||
'txcount': 9,
|
||||
'unconfirmed_balance': Decimal('0E-8'),
|
||||
'walletname': '',
|
||||
'walletversion': 169900}
|
||||
---------------------------------------------------------------
|
||||
```
|
||||
Outros comandos informativos como `getblockchaininfo`, `getnetworkinfo`, `getpeerinfo` e `getblockchaininfo` funcionarão de forma semelhante.
|
||||
|
||||
Outros comandos podem fornecer informações específicas sobre elementos selecionados na sua carteira.
|
||||
|
||||
### Recuperando um Array
|
||||
|
||||
O RPC `listtransactions` permite que observemos as 10 transações mais recentes do nosso sistema (ou algum conjunto arbitrário de transações usando os argumentos `count` e `skip`). Ele mostra como um comando RPC pode retornar uma matriz simples de ser manipulada:
|
||||
|
||||
```py
|
||||
tx_list = rpc_client.listtransactions()
|
||||
pprint(tx_list)
|
||||
```
|
||||
|
||||
### Explorando um UTXO
|
||||
|
||||
Da mesma forma, podemos usar o `listunspent` para obter uma matriz de UTXOs:
|
||||
|
||||
```py
|
||||
print("Exploring UTXOs")
|
||||
## List UTXOs
|
||||
utxos = rpc_client.listunspent()
|
||||
print("Utxos: ")
|
||||
print("-----")
|
||||
pprint(utxos)
|
||||
print("------------------------------------------\n")
|
||||
```
|
||||
|
||||
Para manipular uma matriz como a retornada de `listtransactions` ou` listunpsent`, você apenas pega o item apropriado do elemento apropriado da matriz:
|
||||
```
|
||||
## Select a UTXO - first one selected here
|
||||
utxo_txid = utxos[0]['txid']
|
||||
```
|
||||
|
||||
Para o `listunspent`, obtemos um `txid`. Podemos recuperar as informações sobre ele com o comando `gettransaction` e, em seguida, decodificá-lo com um `decoderawtransaction`:
|
||||
|
||||
|
||||
```
|
||||
utxo_hex = rpc_client.gettransaction(utxo_txid)['hex']
|
||||
|
||||
utxo_tx_details = rpc_client.decoderawtransaction(utxo_hex)
|
||||
|
||||
print("Details of Utxo with txid:", utxo_txid)
|
||||
print("---------------------------------------------------------------")
|
||||
print("UTXO Details:")
|
||||
print("------------")
|
||||
pprint(utxo_tx_details)
|
||||
print("---------------------------------------------------------------\n")
|
||||
```
|
||||
|
||||
Este código está disponível no arquivo [walletinfo.py](src/17_4_walletinfo.py).
|
||||
```
|
||||
$ python3 walletinfo.py
|
||||
---------------------------------------------------------------
|
||||
Wallet Info:
|
||||
-----------
|
||||
{'avoid_reuse': False,
|
||||
'balance': Decimal('0.01031734'),
|
||||
'hdseedid': 'da5a1b058deb9e51ecffef1b0ddc069a5dfb2c5f',
|
||||
'immature_balance': Decimal('0E-8'),
|
||||
'keypoololdest': 1596567843,
|
||||
'keypoolsize': 1000,
|
||||
'keypoolsize_hd_internal': 999,
|
||||
'paytxfee': Decimal('0E-8'),
|
||||
'private_keys_enabled': True,
|
||||
'scanning': False,
|
||||
'txcount': 6,
|
||||
'unconfirmed_balance': Decimal('0E-8'),
|
||||
'walletname': '',
|
||||
'walletversion': 169900}
|
||||
---------------------------------------------------------------
|
||||
|
||||
Utxos:
|
||||
-----
|
||||
[{'address': 'mv9cjEnS2o1EygBMdrz99LzhM8KeEMoXDg',
|
||||
'amount': Decimal('0.00001000'),
|
||||
'confirmations': 1180,
|
||||
'desc': "pkh([ce0c7e14/0'/0'/25']02d0541b9211aecd25913f7fdecfc1b469215fa326d52067b1b3f7efbd12316472)#n06pq9q5",
|
||||
'label': '-addresstype',
|
||||
'safe': True,
|
||||
'scriptPubKey': '76a914a080d1a10f5e7a02d0a291f118982ed19e8cfcd788ac',
|
||||
'solvable': True,
|
||||
'spendable': True,
|
||||
'txid': '84207ffec658ae29ad1fdd330d8a13613303c3cf281ce628fadeb7636ffb535e',
|
||||
'vout': 0},
|
||||
{'address': 'tb1qrcf8c29966tvqxhwrtd2se3rj6jeqtll3r46a4',
|
||||
'amount': Decimal('0.01029734'),
|
||||
'confirmations': 1180,
|
||||
'desc': "wpkh([ce0c7e14/0'/1'/26']02c581259ba7e6aef6d7ea23adb08f7c7f10c4c678f2e097a4074639e7685d4805)#j3pctfhf",
|
||||
'safe': True,
|
||||
'scriptPubKey': '00141e127c28a5d696c01aee1adaa8662396a5902fff',
|
||||
'solvable': True,
|
||||
'spendable': True,
|
||||
'txid': '84207ffec658ae29ad1fdd330d8a13613303c3cf281ce628fadeb7636ffb535e',
|
||||
'vout': 1},
|
||||
{'address': 'mzDxbtYY3LBBBJ6HhaBAtnHv6c51BRBTLE',
|
||||
'amount': Decimal('0.00001000'),
|
||||
'confirmations': 1181,
|
||||
'desc': "pkh([ce0c7e14/0'/0'/23']0377bdd176f985b4af2f6bdbb22c2925b6007b6c07ba171f75e65990c002615e98)#3y6ef6vu",
|
||||
'label': '-addresstype',
|
||||
'safe': True,
|
||||
'scriptPubKey': '76a914cd339342b06042bb986a45e73d56db46acc1e01488ac',
|
||||
'solvable': True,
|
||||
'spendable': True,
|
||||
'txid': '1679bee019c61608340b79810377be2798efd4d2ec3ace0f00a1967af70666b9',
|
||||
'vout': 1}]
|
||||
------------------------------------------
|
||||
|
||||
Details of Utxo with txid: 84207ffec658ae29ad1fdd330d8a13613303c3cf281ce628fadeb7636ffb535e
|
||||
---------------------------------------------------------------
|
||||
UTXO Details:
|
||||
------------
|
||||
{'hash': '0c6c27f58f122329bbc53a91f290b35ce23bd2708706b21a04cdc387dc8e2fd9',
|
||||
'locktime': 1831103,
|
||||
'size': 225,
|
||||
'txid': '84207ffec658ae29ad1fdd330d8a13613303c3cf281ce628fadeb7636ffb535e',
|
||||
'version': 2,
|
||||
'vin': [{'scriptSig': {'asm': '', 'hex': ''},
|
||||
'sequence': 4294967294,
|
||||
'txid': '1679bee019c61608340b79810377be2798efd4d2ec3ace0f00a1967af70666b9',
|
||||
'txinwitness': ['3044022014b3e2359fb46d8cbc4cd30fa991b455edfa4b419a4c64a53fcdfc79e3ca89db022010cefc3268bc252d55f1982c426328b709b47d02332def9e2efb3b12de2cf0d301',
|
||||
'0351b470e87b44e8e9607acf09b8d4543c51c93c17dc741176319e60202091f2be'],
|
||||
'vout': 0}],
|
||||
'vout': [{'n': 0,
|
||||
'scriptPubKey': {'addresses': ['mv9cjEnS2o1EygBMdrz99LzhM8KeEMoXDg'],
|
||||
'asm': 'OP_DUP OP_HASH160 '
|
||||
'a080d1a10f5e7a02d0a291f118982ed19e8cfcd7 '
|
||||
'OP_EQUALVERIFY OP_CHECKSIG',
|
||||
'hex': '76a914a080d1a10f5e7a02d0a291f118982ed19e8cfcd788ac',
|
||||
'reqSigs': 1,
|
||||
'type': 'pubkeyhash'},
|
||||
'value': Decimal('0.00001000')},
|
||||
{'n': 1,
|
||||
'scriptPubKey': {'addresses': ['tb1qrcf8c29966tvqxhwrtd2se3rj6jeqtll3r46a4'],
|
||||
'asm': '0 1e127c28a5d696c01aee1adaa8662396a5902fff',
|
||||
'hex': '00141e127c28a5d696c01aee1adaa8662396a5902fff',
|
||||
'reqSigs': 1,
|
||||
'type': 'witness_v0_keyhash'},
|
||||
'value': Decimal('0.01029734')}],
|
||||
'vsize': 144,
|
||||
'weight': 573}
|
||||
---------------------------------------------------------------
|
||||
```
|
||||
|
||||
## Crindo um endereço
|
||||
|
||||
Criar um novo endereço com Python 3 requer apenas o uso de um RPC como `getnewaddress` ou `getrawchangeaddress`.
|
||||
```
|
||||
new_address = rpc_client.getnewaddress("Learning-Bitcoin-from-the-Command-Line")
|
||||
new_change_address = rpc_client.getrawchangeaddress()
|
||||
```
|
||||
Neste exemplo, usamos comando `getnewaddress` junto com um argumento: o rótulo `Learning-Bitcoin-from-the-Command-Line`.
|
||||
|
||||
## Enviando uma transação
|
||||
|
||||
A criação de uma transação no Python 3 requer a combinação de alguns dos exemplos anteriores, de criação de endereços e de recuperação de UTXOs, com alguns comandos novos do RPC para criar, assinar e enviar uma transação, da mesma forma que fizemos anteriormente na linha de comando.
|
||||
|
||||
Existem cinco etapas:
|
||||
|
||||
0. Criar dois endereços, um que funcionará como destinatário e outro para o troco;
|
||||
1. Selecionar um UTXO e definir os detalhes da transação;
|
||||
2. Criar uma transação bruta;
|
||||
3. Assinar a transação bruta com a chave privada do UTXO;
|
||||
4. Transmitir a transação na Testnet do bitcoin.
|
||||
|
||||
### 1. Selecionando o UTXO e definindo os detalhes da transação
|
||||
|
||||
No trecho de código a seguir, primeiro selecionaremos o UTXO que gostaríamos de gastar. Em seguida, iremos obter o endereço, id da transação e o índice vetorial da saída.
|
||||
|
||||
```py
|
||||
utxos = rpc_client.listunspent()
|
||||
selected_utxo = utxos[0] # again, selecting the first utxo here
|
||||
utxo_address = selected_utxo['address']
|
||||
utxo_txid = selected_utxo['txid']
|
||||
utxo_vout = selected_utxo['vout']
|
||||
utxo_amt = float(selected_utxo['amount'])
|
||||
```
|
||||
Em seguida, também recuperamos o endereço do destinatário para o qual desejamos enviar os bitcoins, calculamos a quantidade de bitcoins que desejamos enviar e calculamos a taxa de minerador e o valor do troco. Aqui, o valor é dividido arbitrariamente em dois e uma taxa de minerador é definida arbitrariamente.
|
||||
|
||||
```py
|
||||
recipient_address = new_address
|
||||
recipient_amt = utxo_amt / 2 # sending half coins to recipient
|
||||
miner_fee = 0.00000300 # choose appropriate fee based on your tx size
|
||||
change_address = new_change_address
|
||||
change_amt = float('%.8f'%((utxo_amt - recipient_amt) - miner_fee))
|
||||
```
|
||||
|
||||
> :warning: **AVISO:** Obviamente, um programa real faria escolhas mais sofisticadas sobre qual UTXO usar, o que fazer com os fundos e que taxa de mineração pagar.
|
||||
|
||||
### 2. Criando uma transação bruta
|
||||
|
||||
Agora temos todas as informações para enviar uma transação, mas antes de enviá-la, devemos criar uma transação.
|
||||
|
||||
```py
|
||||
txids_vouts = [{"txid": utxo_txid, "vout": utxo_vout}]
|
||||
addresses_amts = {f"{recipient_address}": recipient_amt, f"{change_address}": change_amt}
|
||||
unsigned_tx_hex = rpc_client.createrawtransaction(txids_vouts, addresses_amts)
|
||||
```
|
||||
Lembre-se de que o formato do comando `createrawtransaction` é:
|
||||
|
||||
```$ bitcoin-cli createrawtransaction '[{"txid": <utxo_txid>, "vout": <vector_id>}]' '{"<address>": <amount>}'```
|
||||
|
||||
O `txids_vouts` é, portanto, uma lista e o `address_amts` é um dicionário python, para combinar com o formato de `createrawtransaction`.
|
||||
|
||||
Se quisermos ver mais sobre os detalhes da transação que criamos, podemos usar `decoderawtransaction`, tanto no Python 3 quanto com `bitcoin-cli`.
|
||||
|
||||
### 3. Assinar transação bruta
|
||||
|
||||
Assinar uma transação geralmente é a parte mais complicada do envio de uma transação, quando olhamos a parte de programação. Aqui recuperamos uma chave privada de um endereço com `dumpprivkey` e a coloca em uma matriz:
|
||||
```py
|
||||
address_priv_key = [] # list of priv keys of each utxo
|
||||
address_priv_key.append(rpc_client.dumpprivkey(utxo_address))
|
||||
```
|
||||
Depois, usamos esse array, que deve conter as chaves privadas de cada UTXO que está sendo gasto, para assinar nosso `unsigned_tx_hex`:
|
||||
```py
|
||||
signed_tx = rpc_client.signrawtransactionwithkey(unsigned_tx_hex, address_priv_key)
|
||||
```
|
||||
Isso retornará um objeto JSON com o hex da transação assinada e, se foi assinado completamente ou não:
|
||||
|
||||
### 4. Transmitindo a Transação
|
||||
|
||||
Finalmente, estamos prontos para transmitir a transação assinada na rede Bitcoin:
|
||||
|
||||
```py
|
||||
send_tx = rpc_client.sendrawtransaction(signed_tx['hex'])
|
||||
```
|
||||
### Executando nosso código
|
||||
|
||||
[Este código](src/17_4_sendtx.py) está cheio de instruções com `print` para demonstrar todos os dados disponíveis em cada momento:
|
||||
```
|
||||
$ python3 sendtx.py
|
||||
Creating a Transaction
|
||||
---------------------------------------------------------------
|
||||
Transaction Details:
|
||||
-------------------
|
||||
UTXO Address.......: mv9cjEnS2o1EygBMdrz99LzhM8KeEMoXDg
|
||||
UTXO Txid..........: 84207ffec658ae29ad1fdd330d8a13613303c3cf281ce628fadeb7636ffb535e
|
||||
Vector ID of Output: 0
|
||||
UTXO Amount........: 1e-05
|
||||
Tx Amount..........: 5e-06
|
||||
Recipient Address..: tb1qca0elxxqzw5xc0s3yq5qhapzzj90ka0zartu6y
|
||||
Change Address.....: tb1qrveukqrvqm9h6fua99xvcxgnvdx507dg8e8hrt
|
||||
Miner Fee..........: 3e-06
|
||||
Change Amount......: 2e-06
|
||||
---------------------------------------------------------------
|
||||
|
||||
---------------------------------------------------------------
|
||||
Unsigned Transaction Hex: 02000000015e53fb6f63b7defa28e61c28cfc3033361138a0d33dd1fad29ae58c6fe7f20840000000000ffffffff02f401000000000000160014c75f9f98c013a86c3e1120280bf422148afb75e2c8000000000000001600141b33cb006c06cb7d279d294ccc1913634d47f9a800000000
|
||||
---------------------------------------------------------------
|
||||
|
||||
---------------------------------------------------------------
|
||||
Signed Transaction:
|
||||
----------------------
|
||||
{'complete': True,
|
||||
'hex': '02000000015e53fb6f63b7defa28e61c28cfc3033361138a0d33dd1fad29ae58c6fe7f2084000000006a47304402205da9b2234ea057c9ef3b7794958db6c650c72dedff1a90d2915147a5f6413f2802203756552aba0dd8ebd71b0f28341becc01b28d8b28af063d7c8ce89f9c69167f8012102d0541b9211aecd25913f7fdecfc1b469215fa326d52067b1b3f7efbd12316472ffffffff02f401000000000000160014c75f9f98c013a86c3e1120280bf422148afb75e2c8000000000000001600141b33cb006c06cb7d279d294ccc1913634d47f9a800000000'}
|
||||
---------------------------------------------------------------
|
||||
|
||||
---------------------------------------------------------------
|
||||
TXID of sent transaction: 187f8baa222f9f37841d966b6bad59b8131cfacca861cbe9bfc8656bd16a44cc
|
||||
```
|
||||
|
||||
## Resumo: Acessando o Bitcoind com Python
|
||||
|
||||
Acessar Bitcoind com Python é muito fácil usando uma biblioteca `python-bitcoinrpc`. A primeira coisa a se fazer, é estabelecer uma conexão com nossa instância `bitcoind`, então podemos fazer todas as chamadas API bitcoin conforme descrito nos documentos do Bitcoin Core. Isso torna mais fácil criar programas pequenos ou grandes para gerenciar nosso próprio node, verificando saldos ou criando aplicações interessantes, conforme acessamos todo o poder do `bitcoin-cli`.
|
||||
|
||||
## O Que Vem Depois?
|
||||
|
||||
Vamos aprender mais sobre "Conversando com o Bitcoind com Outras Linguagens" na seção [§17.5: Acessando o Bitcoind com Rust](17_5_Accessing_Bitcoind_with_Rust.md).
|
||||
|
||||
## Variante: Construindo o Python a partir do código-fonte
|
||||
|
||||
Se precisarmos instalar o Python 3 a partir do código-fonte, precisaremos seguir estas instruções e continuar da parte ["Criando um projeto BitcoinRPC"](17_4_Accessing_Bitcoind_with_Python.md#Creating-a-bitcoinrpc-project).
|
||||
|
||||
### 1. Instalando as dependências
|
||||
```sh
|
||||
$ sudo apt-get install build-essential checkinstall
|
||||
$ sudo apt-get install libreadline-gplv2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libffi-dev zlib1g-dev
|
||||
```
|
||||
|
||||
### 2. Baixando e extraindo o Python
|
||||
```sh
|
||||
$ wget https://www.python.org/ftp/python/3.8.3/Python-3.8.3.tgz
|
||||
$ tar -xzf Python-3.8.3.tgz
|
||||
```
|
||||
|
||||
### 3. Compilando o código-fonte Python e verificando a instalação:
|
||||
|
||||
```sh
|
||||
$ cd Python-3.8.3
|
||||
$ sudo ./configure --enable-optimizations
|
||||
$ sudo make -j 8 # enter the number of cores of your system you want to use to speed up the build process.
|
||||
$ sudo make altinstall
|
||||
$ python3.8 --version
|
||||
```
|
||||
|
||||
Depois de obter o retorno da versão, podemos remover o arquivo de instalação:
|
||||
```sh
|
||||
$ rm Python-3.8.3.tgz
|
||||
```
|
362
pt/17_5_Accessing_Bitcoind_with_Rust.md
Normal file
362
pt/17_5_Accessing_Bitcoind_with_Rust.md
Normal file
@ -0,0 +1,362 @@
|
||||
# 17.5: Acessando o Bitcoind com Rust
|
||||
|
||||
> :information_source: **NOTA:** Esta seção foi adicionada recentemente ao curso e é um rascunho inicial que ainda pode estar aguardando revisão.
|
||||
|
||||
Esta seção explica como interagir com o `bitcoind` usando a linguagem de programação Rust e o [`bitcoincore-rpc` crate](https://github.com/rust-bitcoin/rust-bitcoincore-rpc).
|
||||
|
||||
## Configurando o Rust
|
||||
|
||||
Precisaremos instalar o Rust e o Cargo.
|
||||
|
||||
Eles podem ser instalados via `curl`. Basta usar a instalação "padrão":
|
||||
```vim
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
Se tudo correr bem, veremos:
|
||||
```vim
|
||||
Rust is installed now. Great!
|
||||
```
|
||||
Em seguida, precisaremos fazer o logout e o login novamente ou então adicionar o diretório binário do Cargo ao nosso caminho manualmente:
|
||||
```
|
||||
$ source $HOME/.cargo/env
|
||||
```
|
||||
|
||||
### Configurando o `bitcoincore-rpc`
|
||||
|
||||
Para a maioria das linguagens de programação, precisamos instalar uma biblioteca Bitcoin RPC antes de criar nosso primeiro projeto, mas aqui iremos fazer como parte da criação do nosso projeto.
|
||||
|
||||
### Criando um projeto `bitcoincore-rpc`
|
||||
|
||||
Podemos criar um novo projeto usando `cargo new btc_test`:
|
||||
|
||||
```
|
||||
$ cargo new btc_test
|
||||
Created binary (application) `btc_test` package
|
||||
```
|
||||
|
||||
Isso criará um diretório `btc_test` que contém um exemplo de código-fonte "hello world" que está no `src/main.rs` e um arquivo `Cargo.toml`.
|
||||
|
||||
Vamos compilar e executar nosso código com `cargo run`:
|
||||
```
|
||||
$ cd btc_test
|
||||
$ cargo run
|
||||
Compiling btc_test v0.1.0 (/home/standup/btc_test)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.14s
|
||||
Running `target/debug/btc_test`
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
> :information_source: **NOTA:** se encontrarmos o erro `linker‘ cc ’not found`, teremos que instalar um
|
||||
Compilador C. Se estivermos no Linux, podemos instalar com as [ferramentas de desenvolvimento](https://www.ostechnix.com/install-development-tools-linux/).
|
||||
|
||||
Para acessar o crate (biblioteca) `bitcoincore-rpc`, devemos adicioná-la ao nosso arquivo `Cargo.toml` na seção `dependencies`:
|
||||
|
||||
```rust
|
||||
[dependencies]
|
||||
bitcoincore-rpc = "0.11.0"
|
||||
```
|
||||
|
||||
Quando usarmos o comando `cargo run` novamente, ele irá instalar os crates e suas (numerosas) dependências.
|
||||
```
|
||||
$ cargo run
|
||||
Updating crates.io index
|
||||
...
|
||||
Compiling bitcoin v0.23.0
|
||||
Compiling bitcoincore-rpc-json v0.11.0
|
||||
Compiling bitcoincore-rpc v0.11.0
|
||||
Compiling btc_test v0.1.0 (/home/standup/btc_test)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 23.56s
|
||||
Running `target/debug/btc_test`
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Quando estiver usando o `bitcoin-rpc`, normalmente precisaremos incluir o seguinte:
|
||||
```
|
||||
use bitcoincore_rpc::{Auth, Client, RpcApi};
|
||||
```
|
||||
|
||||
## Construindo nossa conexão
|
||||
|
||||
Para criarmos um `RPC client` do Bitcoin, vamos modificar o `src/main.rs`:
|
||||
|
||||
|
||||
```rust
|
||||
use bitcoincore_rpc::{Auth, Client, RpcApi};
|
||||
|
||||
fn main() {
|
||||
let rpc = Client::new(
|
||||
"http://localhost:18332".to_string(),
|
||||
Auth::UserPass("StandUp".to_string(), "password".to_string()),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
Como de costume, vamos nos certificar de inserir nosso nome de usuário e senha corretos no `~/.bitcoin/bitcoin.conf`. Aqui, eles serão usados como argumentos para `Auth :: UserPass`.
|
||||
|
||||
> :link: **TESTNET vs MAINNET:** E, como de costume, vamos usar a porta 8332 para a Mainnet.
|
||||
|
||||
Quando terminar, também deve fechar nossa conexão:
|
||||
```rust
|
||||
let _ = rpc.stop().unwrap();
|
||||
```
|
||||
|
||||
O `cargo run` deve compilar e executar o exemplo com sucesso com um aviso `warning: unused variable: rpc`
|
||||
|
||||
### Fazendo uma chamada RPC
|
||||
|
||||
As chamadas RPC são feitas usando o `rpc Client` que criamos:
|
||||
|
||||
```rust
|
||||
let mining_info = rpc.get_mining_info().unwrap();
|
||||
println!("{:#?}", mining_info);
|
||||
```
|
||||
Geralmente, as palavras na chamada RPC são separadas por `_`s. Uma lista completa está disponível na [documentação do crate](https://crates.io/crates/bitcoincore-rpc).
|
||||
|
||||
### Fazendo uma chamada RPC com argumentos
|
||||
|
||||
O envio de uma chamada RPC com argumentos usando Rust requer apenas o conhecimento de como a função é apresentada. Por exemplo, a função `get_block` é definida da seguinte forma na [documentação](https://docs.rs/bitcoincore-rpc/0.11.0/bitcoincore_rpc/trait.RpcApi.html#method.get_block):
|
||||
|
||||
```rust
|
||||
fn get_block(&self, hash: &BlockHash) -> Result<Block>
|
||||
```
|
||||
Só precisamos permitir que ele pegue emprestado um blockhash, que pode ser recuperado (por exemplo) usando o comando `get_best_block_hash`.
|
||||
|
||||
Aqui está o código completo para recuperar um hash de bloco, transformando-o em um bloco e imprimi-lo.
|
||||
```
|
||||
let hash = rpc.get_best_block_hash().unwrap();
|
||||
let block = rpc.get_block(&hash).unwrap();
|
||||
|
||||
println!("{:?}", block);
|
||||
```
|
||||
|
||||
> **NOTA:** Outra chamada possível que consideramos para esta seção é a `get_address_info`, mas, infelizmente, no momento em que este livro foi escrito, a função `bitcoincore-rpc` não funciona com versões recentes do Bitcoin Core devido ao crate não abordar as mais recentes mudanças de API no Bitcoin Core. Esperamos que isso seja resolvido no próximo lançamento do crate, mas enquanto isso, damos essa atenção ao programador.
|
||||
|
||||
### Executando nosso código
|
||||
|
||||
Podemos acessar o [código src](src/17_5_main-getinfo.rs) e executá-lo. Infelizmente, a informação do "Block" sairá um pouco feia porque este exemplo não inclui uma biblioteca para embelezá-la.
|
||||
|
||||
```
|
||||
$ cargo run
|
||||
Compiling btc_test v0.1.0 (/home/standup/btc_test)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 1.61s
|
||||
Running `target/debug/btc_test`
|
||||
GetMiningInfoResult {
|
||||
blocks: 1832335,
|
||||
current_block_weight: None,
|
||||
current_block_tx: None,
|
||||
difficulty: 4194304.0,
|
||||
network_hash_ps: 77436285865245.1,
|
||||
pooled_tx: 4,
|
||||
chain: "test",
|
||||
warnings: "Warning: unknown new rules activated (versionbit 28)",
|
||||
}
|
||||
Block { header: BlockHeader { version: 541065216, prev_blockhash: 000000000000027715981d5a3047daf6819ea3b8390b73832587594a2074cbf5, merkle_root: 4b2e2c2754b6ed9cf5c857a66ed4c8642b6f6b33b42a4859423e4c3dca462d0c, time: 1599602277, bits: 436469756, nonce: 218614401 }, txdata: [Transaction { version: 1, lock_time: 0, input: [TxIn { previous_output: OutPoint { txid: 0000000000000000000000000000000000000000000000000000000000000000, vout: 4294967295 }, script_sig: Script(OP_PUSHBYTES_3 8ff51b OP_PUSHBYTES_22 315448617368263538434f494e1d00010320a48db852 OP_PUSHBYTES_32 <push past end>), sequence: 4294967295, witness: [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] }], output: [TxOut { value: 19721777, script_pubkey: Script(OP_HASH160 OP_PUSHBYTES_20 011beb6fb8499e075a57027fb0a58384f2d3f784 OP_EQUAL) }, TxOut { value: 0, script_pubkey: Script(OP_RETURN OP_PUSHBYTES_36 aa21a9ed63363f3620ab5e38b8860a50c84050e5ec31af3636bbd73f01ba9f14103100ee) }] }, Transaction { version: 2, lock_time: 1832282, input: [TxIn { previous_output: OutPoint { txid: cbf880f73d421baf0aa4f0d28e63ba00e5bc6bd934b91eb0641354ce5ca42f7e, vout: 0 }, script_sig: Script(OP_PUSHBYTES_22 00146b8dbd32e5deb90d22934e1513bae6e70156cd50), sequence: 4294967294, witness: [[48, 68, 2, 32, 13, 89, 205, 30, 67, 24, 196, 83, 65, 224, 44, 138, 98, 58, 81, 135, 132, 209, 23, 166, 23, 44, 3, 228, 95, 102, 166, 214, 62, 38, 155, 147, 2, 32, 119, 2, 34, 246, 148, 255, 166, 10, 90, 52, 242, 32, 74, 241, 123, 148, 89, 199, 197, 3, 152, 134, 242, 215, 109, 61, 241, 241, 13, 70, 86, 207, 1], [2, 192, 145, 170, 206, 55, 4, 36, 138, 145, 217, 50, 19, 73, 130, 136, 245, 131, 184, 142, 239, 75, 13, 67, 17, 177, 57, 86, 151, 139, 89, 35, 109]] }], output: [TxOut { value: 1667908, script_pubkey: Script(OP_HASH160 OP_PUSHBYTES_20 908ca2b8b49ccf53efa2226afa85f6cc58dfd7e7 OP_EQUAL) }, TxOut { value: 9093, script_pubkey: Script(OP_DUP OP_HASH160 OP_PUSHBYTES_20 42ee67664ce16edefc68ad0e4c5b7ce2fc2ccc18 OP_EQUALVERIFY OP_CHECKSIG) }] }, ...] }
|
||||
```
|
||||
|
||||
## Pesquisando os fundos
|
||||
|
||||
Podemos procurar fundos sem argumentos opcionais usando a função `get_balance`:
|
||||
|
||||
```rust
|
||||
let balance = rpc.get_balance(None, None).unwrap();
|
||||
println!("Balance: {:?} BTC", balance.as_btc());
|
||||
```
|
||||
Conforme mostrado, a função `as_btc()` ajuda a gerar o saldo em um formato legível:
|
||||
```
|
||||
Balance: 3433.71692741 BTC
|
||||
```
|
||||
|
||||
## Criando um endereço
|
||||
|
||||
A criação de um endereço demonstra como fazer uma chamada RPC com vários argumentos opcionais especificados (por exemplo, um rótulo e um tipo de endereço).
|
||||
|
||||
```rust
|
||||
// Generate a new address
|
||||
let myaddress = rpc
|
||||
.get_new_address(Option::Some("BlockchainCommons"), Option::Some(json::AddressType::Bech32))
|
||||
.unwrap();
|
||||
println!("address: {:?}", myaddress);
|
||||
```
|
||||
Isso também exigirá que tragamos a definição `json` para o escopo:
|
||||
```rust
|
||||
use bitcoincore_rpc::{json, Auth, Client, RpcApi};
|
||||
```
|
||||
|
||||
## Enviando uma transação
|
||||
|
||||
Agora temos tudo de que precisamos para criar uma transação, o que será feito em cinco partes:
|
||||
|
||||
1. Listar UTXOs;
|
||||
2. Preencher variáveis;
|
||||
3. Criar transação bruta;
|
||||
4. Assinar transação;
|
||||
5. Enviar transação.
|
||||
|
||||
### 1. Listando o UTXOs
|
||||
|
||||
Para iniciar a criação de uma transação, primeiro encontramos um UTXO para usar. O seguinte leva o primeiro UTXO com pelo menos 0,01 BTC:
|
||||
|
||||
|
||||
```rust
|
||||
let unspent = rpc
|
||||
.list_unspent(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Option::Some(json::ListUnspentQueryOptions {
|
||||
minimum_amount: Option::Some(Amount::from_btc(0.01).unwrap()),
|
||||
maximum_amount: None,
|
||||
maximum_count: None,
|
||||
minimum_sum_amount: None,
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let selected_tx = &unspent[0];
|
||||
|
||||
println!("selected unspent transaction: {:#?}", selected_tx);
|
||||
```
|
||||
Isso exigirá trazer mais estruturas para o escopo:
|
||||
|
||||
```rust
|
||||
use bitcoincore_rpc::bitcoin::{Address, Amount};
|
||||
```
|
||||
|
||||
Podemos observar que estamos passando cinco variáveis ao comando `list_unspent`. Os primeiros quatro (`minconf`,` maxconf`, `address` e `include_unsafe`) não são usados aqui. O quinto é o `query_options`, que não usamos antes, mas temos algumas opções de filtragem poderosas, incluindo a capacidade de olhar apenas para os UTXOs com um certo valor, mínimo ou máximo.
|
||||
|
||||
### 2. Preenchendo as variáveis
|
||||
|
||||
Para começar a preencher as variáveis que precisaremos para criar uma nova transação, criamos a entrada do `txid` e do `vout` ao UTXO que selecionamos:
|
||||
```rust
|
||||
let selected_utxos = json::CreateRawTransactionInput {
|
||||
txid: selected_tx.txid,
|
||||
vout: selected_tx.vout,
|
||||
sequence: None,
|
||||
};
|
||||
```
|
||||
Em seguida, podemos calcular a quantia que vamos gastar subtraindo uma taxa de mineração dos fundos no UTXO:
|
||||
```
|
||||
// send all bitcoin in the UTXO except a minor value which will be paid to miners
|
||||
let unspent_amount = selected_tx.amount;
|
||||
let amount = unspent_amount - Amount::from_btc(0.00001).unwrap();
|
||||
```
|
||||
Por fim, podemos criar um mapa do hash do endereço e da quantidade para formar a saída:
|
||||
```
|
||||
let mut output = HashMap::new();
|
||||
output.insert(
|
||||
myaddress.to_string(),
|
||||
amount,
|
||||
);
|
||||
```
|
||||
Outra característica é necessária para a variável de saída: O `HashMap`. Ele permite que armazenemos
|
||||
valores por chave, que iremos precisar para representar a informação `{address: amount}`.
|
||||
|
||||
```rust
|
||||
use std::collections::HashMap;
|
||||
```
|
||||
|
||||
### 3. Criando a transação bruta
|
||||
|
||||
Agora, estamos prontos para criar uma transação bruta:
|
||||
```rust
|
||||
let unsigned_tx = rpc
|
||||
.create_raw_transaction(&[selected_utxos], &output, None, None)
|
||||
.unwrap();
|
||||
```
|
||||
### 4. Assinando a transação
|
||||
|
||||
A assinatura da transação pode ser feita com um simples uso do `sign_raw_transaction_with_wallet`:
|
||||
```rust
|
||||
let signed_tx = rpc
|
||||
.sign_raw_transaction_with_wallet(&unsigned_tx, None, None)
|
||||
.unwrap();
|
||||
|
||||
println!("signed tx {:?}", signed_tx.transaction().unwrap());
|
||||
```
|
||||
|
||||
### 5. Enviando a transação
|
||||
|
||||
Finalmente, podemos transmitir a transação:
|
||||
```rust
|
||||
let txid_sent = rpc
|
||||
.send_raw_transaction(&signed_tx.transaction().unwrap())
|
||||
.unwrap();
|
||||
|
||||
println!("{:?}", txid_sent);
|
||||
```
|
||||
|
||||
### Executando nosso código
|
||||
|
||||
Agora podemos executar o código completo do [src](src/17_5_main-sendtx.rs).
|
||||
|
||||
|
||||
```
|
||||
$ cargo run
|
||||
Compiling btc_test v0.1.0 (/home/standup/btc_test)
|
||||
warning: unused variable: `unspent_amount`
|
||||
--> src/main.rs:86:9
|
||||
|
|
||||
86 | let unspent_amount = selected_tx.amount;
|
||||
| ^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unspent_amount`
|
||||
|
|
||||
= note: `#[warn(unused_variables)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 2.11s
|
||||
Running `target/debug/btc_test`
|
||||
Balance: 0.01031434 BTC
|
||||
address: tb1qx5jz36xgt9q2rkh4daee8ewfj0g5z05v8qsua2
|
||||
selected unspent transaction: ListUnspentResultEntry {
|
||||
txid: 84207ffec658ae29ad1fdd330d8a13613303c3cf281ce628fadeb7636ffb535e,
|
||||
vout: 1,
|
||||
address: Some(
|
||||
tb1qrcf8c29966tvqxhwrtd2se3rj6jeqtll3r46a4,
|
||||
),
|
||||
label: None,
|
||||
redeem_script: None,
|
||||
witness_script: None,
|
||||
script_pub_key: Script(OP_0 OP_PUSHBYTES_20 1e127c28a5d696c01aee1adaa8662396a5902fff),
|
||||
amount: Amount(1029734 satoshi),
|
||||
confirmations: 1246,
|
||||
spendable: true,
|
||||
solvable: true,
|
||||
descriptor: Some(
|
||||
"wpkh([ce0c7e14/0\'/1\'/26\']02c581259ba7e6aef6d7ea23adb08f7c7f10c4c678f2e097a4074639e7685d4805)#j3pctfhf",
|
||||
),
|
||||
safe: true,
|
||||
}
|
||||
unsigned tx Transaction {
|
||||
version: 2,
|
||||
lock_time: 0,
|
||||
input: [
|
||||
TxIn {
|
||||
previous_output: OutPoint {
|
||||
txid: 84207ffec658ae29ad1fdd330d8a13613303c3cf281ce628fadeb7636ffb535e,
|
||||
vout: 1,
|
||||
},
|
||||
script_sig: Script(),
|
||||
sequence: 4294967295,
|
||||
witness: [],
|
||||
},
|
||||
],
|
||||
output: [
|
||||
TxOut {
|
||||
value: 1028734,
|
||||
script_pubkey: Script(OP_0 OP_PUSHBYTES_20 352428e8c85940a1daf56f7393e5c993d1413e8c),
|
||||
},
|
||||
],
|
||||
}
|
||||
signed tx Transaction { version: 2, lock_time: 0, input: [TxIn { previous_output: OutPoint { txid: 84207ffec658ae29ad1fdd330d8a13613303c3cf281ce628fadeb7636ffb535e, vout: 1 }, script_sig: Script(), sequence: 4294967295, witness: [[48, 68, 2, 32, 98, 230, 199, 113, 156, 242, 158, 42, 148, 229, 239, 44, 9, 226, 127, 219, 72, 51, 26, 135, 44, 212, 179, 200, 213, 63, 56, 167, 0, 55, 236, 235, 2, 32, 41, 43, 30, 109, 60, 162, 124, 67, 20, 126, 4, 107, 124, 95, 9, 200, 132, 246, 147, 235, 176, 55, 59, 45, 190, 18, 211, 201, 143, 62, 163, 36, 1], [2, 197, 129, 37, 155, 167, 230, 174, 246, 215, 234, 35, 173, 176, 143, 124, 127, 16, 196, 198, 120, 242, 224, 151, 164, 7, 70, 57, 231, 104, 93, 72, 5]] }], output: [TxOut { value: 1028734, script_pubkey: Script(OP_0 OP_PUSHBYTES_20 352428e8c85940a1daf56f7393e5c993d1413e8c) }] }
|
||||
b0eda3517e6fac69e58ae315d7fe7a1981e3a858996cc1e3135618cac9b79d1a
|
||||
```
|
||||
|
||||
## Resumo: Acessando o Bitcoind com Rust
|
||||
|
||||
O `bitcoincore-rpc` é um crate simples e robusto que permitirá que possamos interagir com o Bitcoin RPC usando Rust. No entanto, no momento em que este livro foi escrito, ele ficou para trás no Bitcoin Core, o que pode causar alguns problemas de uso.
|
||||
|
||||
## O Que Vem Depois?
|
||||
|
||||
Vamos aprender mais sobre "Conversando com o Bitcoind com Outras Linguagens" na seção [§17.6: Acessando o Bitcoind com Swift](17_6_Accessing_Bitcoind_with_Swift.md).
|
463
pt/17_6_Accessing_Bitcoind_with_Swift.md
Normal file
463
pt/17_6_Accessing_Bitcoind_with_Swift.md
Normal file
@ -0,0 +1,463 @@
|
||||
# 17.6: Acessando o Bitcoind com Swift
|
||||
|
||||
> :information_source: **NOTA:** Esta seção foi adicionada recentemente ao curso e é um rascunho inicial que ainda pode estar aguardando revisão.
|
||||
|
||||
Esta seção explica como interagir com o `bitcoind` usando a linguagem de programação Swift e seu próprio cliente RPC.
|
||||
|
||||
## Configurando o Swift no Mac
|
||||
|
||||
Até o momento, construímos todos os nossos ambientes de desenvolvimento de linguagem de programação alternativa em nosso node virtual no Debian. No entanto, essa não é a melhor plataforma para o Swift. Embora haja uma versão do Swift disponível para plataformas Ubuntu, ela não possui todos os recursos e funciona de maneira um pouco diferente do Swift nativo do Mac. Uma "variante" no final desta seção explica como configurá-lo, mas esteja avisado de que estaremos em um território desconhecido.
|
||||
|
||||
Ao invés disso, sugerimos a criação de um ambiente Swift ideal em um Mac. Existem quatro etapas principais para fazer isso.
|
||||
|
||||
### 1. Instalando o Xcode
|
||||
|
||||
Vamos precisar do `Xcode`, o ambiente de desenvolvimento integrado para o Swift e para o Objective-C. Isso pode ser facilmente instalado acessando a Mac App Store e o `Get` Xcode.
|
||||
|
||||
#### Alternativa: Instalando manualmente
|
||||
|
||||
Algumas pessoas desaconselham a instalação da App Store porque é tudo ou nada. Também não funcionará se estivermos usando o Mojave, pois iremos querer evitar as incompatibilidades da Catalina. Nesse caso, podemos fazer o download diretamente da [Área do desenvolvedor](https://developer.apple.com/download/more/) na Apple.
|
||||
|
||||
Se estivermos usando o Mojave, precisaremos do arquivo `xip` para o Xcode 10.3.1. Caso contrário, podemos utilizar o mais recente.
|
||||
|
||||
Depois de baixado, podemos clicar no `xip` para extraí-lo e mover o aplicativo Xcode para a pasta Aplicativos.
|
||||
|
||||
De qualquer forma, devemos ter o Xcode instalado na pasta Aplicativos no final desta etapa.
|
||||
|
||||
### 2. Instalando o servidor Gordian
|
||||
|
||||
Também vamos precisar de um node Bitcoin em nosso Mac, para que possamos nos comunicar com ele. Tecnicamente, poderíamos usar um node remoto e acessá-lo com o login e senha RPC pela rede. No entanto, sugerimos a instalação do full node diretamente no Mac, porque essa é a configuração mais segura e limpa, garantindo que nenhuma das comunicações saia de nossa máquina.
|
||||
|
||||
Para instalar facilmente um full node em nosso Mac, podemos usar Blockchain Commons '[GordianServer for MacOS](https://github.com/BlockchainCommons/GordianServer-macOS). Podemos seguir as [instruções de instalação](https://github.com/BlockchainCommons/GordianServer-macOS#installation-instructions) no README, mas geralmente tudo que precisamos fazer é baixar o arquivo `dmg` atual, abri-lo e instalar a aplicação em nosso diretório de aplicações.
|
||||
|
||||
Depois, vamos executar o aplicativo GordianServer e dizer a ele para `Start` a Testnet.
|
||||
|
||||
> :link: **TESTNET vs. MAINNET:** Ou `Start` na Mainnet.
|
||||
|
||||
#### 3. Tornando nosso bitcoin-cli Gordian acessível
|
||||
|
||||
Quando desejarmos acessar o `bitcoin-cli` criado pelo GordianServer em nosso Mac local, podemos encontrá-lo em `~/.standup/BitcoinCore/bitcoin-0.20.1/bin/bitcoin-cli`.
|
||||
|
||||
Podemos criar um alias para isso:
|
||||
```
|
||||
alias bitcoin-cli="~/.standup/BitcoinCore/bitcoin-0.20.1/bin/bitcoin-cli -testnet"
|
||||
```
|
||||
|
||||
> :link: **TESTNET vs. MAINNET:** Obviamente, o parâmetro `-testnet` só é necessário se estivermos executando a testnet.
|
||||
|
||||
### 4. Encontrando nossas informações do GordianServer
|
||||
|
||||
Finalmente, precisaremos de nossas informações de `rpcuser` e `rpcpassword`. Essas informações estarão em `~/Library/Application Support/Bitcoin/bitcoin.conf` por padrão no Gordian.
|
||||
```
|
||||
$ grep rpc ~/Library/Application\ Support/Bitcoin/bitcoin.conf
|
||||
rpcuser=oIjA53JC2u
|
||||
rpcpassword=ebVCeSyyM0LurvgQyi0exWTqm4oU0rZU
|
||||
...
|
||||
```
|
||||
## Construindo nossa conexão manualmente
|
||||
|
||||
No momento em que este artigo foi escrito, não havia uma biblioteca Bitcoin RPC atualizada e simples de ser usada, que fosse específica para Swift, algo que pudesse entrar e começar a usar imediatamente. Portanto, faremos algo que nunca fizemos antes: Construir uma conexão RPC manualmente.
|
||||
|
||||
### Grave o Transmissor RPC
|
||||
|
||||
Isso requer apenas a escrita de uma função que passe os comandos RPC para o `bitcoind` no formato correto:
|
||||
```
|
||||
func makeCommand(method: String, param: Any, completionHandler: @escaping (Any?) -> Void) -> Void {
|
||||
```
|
||||
|
||||
As conexões RPC para o `bitcoind` usam o protocolo HTML, o que significa que precisamos fazer três coisas: Criar uma URL, fazer um URLRequest e, iniciar uma URLSession.
|
||||
|
||||
#### 1. Criando um URL
|
||||
|
||||
Dentro da função, precisamos criar uma URL a partir do nosso IP, porta, `rpcuser`, `rpcpassword` e wallet:
|
||||
```
|
||||
let testnetRpcPort = "18332"
|
||||
let nodeIp = "127.0.0.1:\(testnetRpcPort)"
|
||||
let rpcusername = "oIjA53JC2u"
|
||||
let rpcpassword = "ebVCeSyyM0LurvgQyi0exWTqm4oU0rZU"
|
||||
let walletName = ""
|
||||
```
|
||||
A conexão RPC real com o Bitcoin Core é construída usando um URL no formato "http://rpcusername:rpcpassword@nodeIp/walletName":
|
||||
```
|
||||
let walletUrl = "http://\(rpcusername):\(rpcpassword)@\(nodeIp)/\(walletName)"
|
||||
|
||||
let url = URL(string: walletUrl)
|
||||
```
|
||||
Isso significa que nossas variáveis de amostra resultam na seguinte URL:
|
||||
```
|
||||
http://oIjA53JC2u:ebVCeSyyM0LurvgQyi0exWTqm4oU0rZU@127.0.0.1:18332/
|
||||
```
|
||||
Que deve se parecer muito com a URL usada em algumas das seções anteriores para conexões RPC.
|
||||
|
||||
#### 2. Criando uma URLRequest
|
||||
|
||||
Com essa URL em mãos, agora podemos criar um URLRequest, com o método `POST` e o tipo de conteúdo `text/plain`. O corpo HTTP será então o objeto JSON familiar que enviamos sempre que nos conectamos diretamente às portas RPC do Bitcoin Core, conforme demonstrado pela primeira vez ao usar o Curl na seção [§4.4](04_4__Interlude_Using_Curl.md).
|
||||
```
|
||||
var request = URLRequest(url: url!)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("text/plain", forHTTPHeaderField: "Content-Type")
|
||||
request.httpBody = "{\"jsonrpc\":\"1.0\",\"id\":\"curltest\",\"method\":\"\(method)\",\"params\":[\(param)]}".data(using: .utf8)
|
||||
```
|
||||
|
||||
#### 3. Criando uma URLSession
|
||||
|
||||
Finalmente, estamos prontos para construir uma URLSession em torno da nossa URLRequest.
|
||||
```
|
||||
let session = URLSession(configuration: .default)
|
||||
let task = session.dataTask(with: request as URLRequest) { data, response, error in
|
||||
```
|
||||
O manipulador de conclusão para `dataTask` precisa verificar se há erros:
|
||||
```
|
||||
do {
|
||||
|
||||
if error != nil {
|
||||
|
||||
//Handle the error
|
||||
|
||||
} else {
|
||||
```
|
||||
E então analisar os dados que estamos recebendo. Aqui, estamos pegando os resultados JSON e colocando-os em um `NSDictionary`:
|
||||
```
|
||||
if let urlContent = data {
|
||||
|
||||
do {
|
||||
|
||||
let json = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableLeaves) as! NSDictionary
|
||||
```
|
||||
Depois disso, há mais manipulação de erros e então podemos eventualmente retornar o `result` do dicionário usando o `CompletHandler` que definimos para a nova função `makeCommand`:
|
||||
```
|
||||
if let errorCheck = json["error"] as? NSDictionary {
|
||||
|
||||
if let errorMessage = errorCheck["message"] as? String {
|
||||
|
||||
print("FAILED")
|
||||
print(errorMessage)
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
let result = json["result"]
|
||||
completionHandler(result)
|
||||
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
//Handle error here
|
||||
|
||||
}
|
||||
```
|
||||
Claro que eventualmente teremos que dizer à `task` para que ela seja iniciada:
|
||||
```
|
||||
task.resume()
|
||||
```
|
||||
E isso é "tudo" o que precisamos fazer nessa interação RPC manual, usando uma linguagem de programação como o Swift.
|
||||
|
||||
> :pray: **AGRADECIMENTOS:** Obrigado a @Fonta1n3 que forneceu o [código principal](https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line/issues/137) para nosso Transmissor RPC.
|
||||
|
||||
### Fazendo uma chamada RPC
|
||||
|
||||
Tendo escrito a função RPC `makeCommand`, podemos enviar uma chamada RPC executando-a. Aqui está `getblockchaininfo`:
|
||||
```
|
||||
let method = "getblockchaininfo"
|
||||
let param = ""
|
||||
|
||||
makeCommand(method: method,param: param) { result in
|
||||
|
||||
print(result!)
|
||||
|
||||
}
|
||||
```
|
||||
### Fazendo uma chamada RPC com argumentos
|
||||
|
||||
Poderíamos da mesma forma obter a contagem de bloco atual a partir dessa informação e usá-la de modo redundante para obter o hash do bloco atual, usando o parâmetro `param`:
|
||||
```
|
||||
let method = "getblockchaininfo"
|
||||
let param = ""
|
||||
|
||||
makeCommand(method: method,param: param) { result in
|
||||
|
||||
let blockinfo = result as! NSDictionary
|
||||
let block = blockinfo["blocks"] as! NSNumber
|
||||
|
||||
let method = "getblockhash"
|
||||
makeCommand(method: method,param: block) { result in
|
||||
print("Blockhash for \(block) is \(result!)")
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### Executando nosso código
|
||||
|
||||
O código completo está disponível no [diretório src/](src / 17_6_getinfo.playground). Carregue-o no playground Xcode e, em seguida, "Editor -> Executar Playground" e devemos obter resultados como:
|
||||
```
|
||||
{
|
||||
bestblockhash = 00000000000000069725608ebc5b59e520572a8088cbc57ffa5ba87b7f300ac7;
|
||||
blocks = 1836745;
|
||||
chain = test;
|
||||
chainwork = 0000000000000000000000000000000000000000000001cc3e9f8e0bc6b71196;
|
||||
difficulty = "16508683.81195478";
|
||||
headers = 1836745;
|
||||
initialblockdownload = 0;
|
||||
mediantime = 1601416765;
|
||||
pruned = 0;
|
||||
"size_on_disk" = 28205538354;
|
||||
softforks = {
|
||||
bip34 = {
|
||||
active = 1;
|
||||
height = 21111;
|
||||
type = buried;
|
||||
};
|
||||
bip65 = {
|
||||
active = 1;
|
||||
height = 581885;
|
||||
type = buried;
|
||||
};
|
||||
bip66 = {
|
||||
active = 1;
|
||||
height = 330776;
|
||||
type = buried;
|
||||
};
|
||||
csv = {
|
||||
active = 1;
|
||||
height = 770112;
|
||||
type = buried;
|
||||
};
|
||||
segwit = {
|
||||
active = 1;
|
||||
height = 834624;
|
||||
type = buried;
|
||||
};
|
||||
};
|
||||
verificationprogress = "0.999999907191804";
|
||||
warnings = "Warning: unknown new rules activated (versionbit 28)";
|
||||
}
|
||||
Blockhash for 1836745 is 00000000000000069725608ebc5b59e520572a8088cbc57ffa5ba87b7f300ac7
|
||||
```
|
||||
## Pesquisando fundos
|
||||
|
||||
Com nosso novo `makeCommand` para funções RPC, podemos executar um comando como `getwalletinfo` ou `getbalance`:
|
||||
```
|
||||
var method = "getwalletinfo"
|
||||
var param = ""
|
||||
|
||||
makeCommand(method: method,param: param) { result in
|
||||
|
||||
print(result!)
|
||||
|
||||
}
|
||||
|
||||
method = "getbalance"
|
||||
makeCommand(method: method,param: param) { result in
|
||||
|
||||
let balance = result as! NSNumber
|
||||
print("Balance is \(balance)")
|
||||
|
||||
}
|
||||
```
|
||||
Que irá retornar:
|
||||
```
|
||||
Balance is 0.01
|
||||
{
|
||||
"avoid_reuse" = 0;
|
||||
balance = "0.01";
|
||||
hdseedid = bf493318f548df8e25c390d6a7f70758fd6b3668;
|
||||
"immature_balance" = 0;
|
||||
keypoololdest = 1599723938;
|
||||
keypoolsize = 999;
|
||||
"keypoolsize_hd_internal" = 1000;
|
||||
paytxfee = 0;
|
||||
"private_keys_enabled" = 1;
|
||||
scanning = 0;
|
||||
txcount = 1;
|
||||
"unconfirmed_balance" = 0;
|
||||
walletname = "";
|
||||
walletversion = 169900;
|
||||
}
|
||||
```
|
||||
## Criando um endereço
|
||||
|
||||
Criar um endereço é bastante simples, mas que tal criar um endereço legado com um rótulo específico? Isso requer dois parâmetros na nossa chamada RPC.
|
||||
|
||||
Uma vez que a simples função `makeCommand` desta seção apenas passa nossos `param`s como as entranhas de um objeto JSON, tudo o que precisamos fazer é formatar corretamente essas entranhas. Esta é uma maneira para fazermos isso:
|
||||
```
|
||||
method = "getnewaddress"
|
||||
param = "\"learning-bitcoin\", \"legacy\""
|
||||
|
||||
makeCommand(method: method,param: param) { result in
|
||||
|
||||
let address = result as! NSString
|
||||
print(address)
|
||||
}
|
||||
```
|
||||
Executá-lo no playground do Xcode produz o seguinte resultado:
|
||||
```
|
||||
mt3ZRsmXHVMMqYQPJ8M74QjF78bmqrdHZF
|
||||
```
|
||||
Esse resultado é obviamente um endereço legado, op rótulo pode então ser verificado na linha de comando:
|
||||
```
|
||||
$ bitcoin-cli getaddressesbylabel "learning-bitcoin"
|
||||
{
|
||||
"mt3ZRsmXHVMMqYQPJ8M74QjF78bmqrdHZF": {
|
||||
"purpose": "receive"
|
||||
}
|
||||
}
|
||||
```
|
||||
Sucesso!
|
||||
|
||||
> :information_source: **NOTA:** Como costumamos dizer nesses exemplos de programação, um programa do mundo real seria muito mais sofisticado. Em particular, gostaríamos de poder enviar um objeto JSON real como um parâmetro e, em seguida, ter no nosso programa um `makeCommand` analisando-o e inserindo-o na URLSession de forma adequada. O que temos aqui maximiza a legibilidade e a simplicidade sem enfocar na facilidade de uso.
|
||||
|
||||
## Enviando uma transação
|
||||
|
||||
Como de costume, o envio de uma transação (da maneira mais difícil) é um processo de várias etapas:
|
||||
|
||||
0. Gerar ou recebar um endereço de recebimento;
|
||||
1. Encontrar um UTXO não gasto;
|
||||
2. Criar uma transação bruta;
|
||||
3. Assinar a transação bruta;
|
||||
4. Enviar a transação bruta.
|
||||
|
||||
Usando o `address` gerado na etapa anterior como nosso destinatário.
|
||||
|
||||
### 1. Encontrando um UTXO não gasto
|
||||
|
||||
O RPC `listunspent` permite que encontremos nosso UTXO:
|
||||
```
|
||||
method = "listunspent"
|
||||
param = ""
|
||||
|
||||
makeCommand(method: method,param: param) { result in
|
||||
|
||||
let unspent = result as! NSArray
|
||||
let utxo = unspent[0] as! NSDictionary
|
||||
|
||||
let txid = utxo["txid"] as! NSString
|
||||
let vout = utxo["vout"] as! NSInteger
|
||||
let amount = utxo["amount"] as! NSNumber
|
||||
let new_amount = amount.floatValue - 0.0001
|
||||
```
|
||||
Como em outros exemplos, vamos arbitrariamente pegar o enésimo UTXO e pegar o `txid`,`vout` e `amount` dele.
|
||||
|
||||
> :information_source **NOTA:** Mais uma vez, um programa real seria muito mais sofisticado.
|
||||
|
||||
### 2. Criando uma transação bruta
|
||||
|
||||
Criar uma transação bruta é a coisa mais complicada, porque precisamos acertar todos os nossos objetos JSON, arrays e aspas. Veja como fazer isso no Swift, usando a formatação `param` muito básica do transmissor:
|
||||
```
|
||||
method = "createrawtransaction"
|
||||
param="[ { \"txid\": \"\(txid)\", \"vout\": \(vout) } ], { \"\(address)\": \(new_amount)}"
|
||||
makeCommand(method: method,param: param) { result in
|
||||
|
||||
let hex = result as! NSString
|
||||
```
|
||||
### 3. Assinando a transação bruta
|
||||
|
||||
Assinar nossa transação requer apenas que executemos o RPC `signrawtransactionwithwallet`, usando nosso novo `hex`:
|
||||
```
|
||||
method = "signrawtransactionwithwallet"
|
||||
param = "\"\(hex)\""
|
||||
|
||||
makeCommand(method: method,param: param) { result in
|
||||
|
||||
let signedhexinfo = result as! NSDictionary
|
||||
let signedhex = signedhexinfo["hex"] as! NSString
|
||||
```
|
||||
|
||||
### 4. Enviando a transação bruta
|
||||
|
||||
Enviar nossa transação é igualmente simples:
|
||||
```
|
||||
method = "sendrawtransaction"
|
||||
param = "\"\(signedhex)\""
|
||||
|
||||
makeCommand(method: method,param: param) { result in
|
||||
|
||||
let new_txid = result as! NSString
|
||||
print("TXID: \(new_txid)")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
O código para este remetente de transação pode ser encontrado no [diretório src/](src/17_6_sendtx.playground).
|
||||
|
||||
## Usando o Swift de outras maneiras
|
||||
|
||||
Isso cobre nossas discussões habituais sobre a programação do Bitcoin RPC usando uma linguagem, mas o Swift é uma linguagem particularmente importante, pois pode ser implantada em dispositivos móveis, um dos principais locais para nossas carteiras. Como tal, podemos querer considerar algumas outras bibliotecas:
|
||||
|
||||
* O Blockchain Commons [ios-Bitcoin framework](https://github.com/BlockchainCommons/iOS-Bitcoin) converte a biblioteca Libbitcoin de C++ para Swift;
|
||||
* [Libwally Swift](https://github.com/blockchain/libwally-swift) é um wrapper Swift para a Libwally.
|
||||
|
||||
## Resumo: Acessando o Bitcoind com Swift
|
||||
|
||||
O Swift é uma linguagem de programação robusta e moderna que infelizmente ainda não tem nenhuma biblioteca RPC fácil de ser utilizada... O que apenas nos deu a oportunidade de escrever uma função de acesso ao RPC. Com isso em mãos, podemos interagir com o `bitcoind` em um Mac ou criar aplicativos complementares em um iPhone, o que é uma combinação perfeita para usar o Bitcoin com airgap.
|
||||
|
||||
## O Que Vem Depois?
|
||||
|
||||
Aprenda sobre o Lightning em [Capítulo 18: Compreendendo a configuração da Lightning](18_0_Understanding_Your_Lightning_Setup.md).
|
||||
|
||||
## Variante: implantando o Swift no Ubuntu
|
||||
|
||||
Se preferirmos implantar o Swift no Ubuntu, pode fazê-lo, embora a funcionalidade não seja a mesma. Parte do código neste capítulo provavelmente gerará erros que precisaremos resolver e também precisaremos trabalhar mais para vincular as bibliotecas C.
|
||||
|
||||
Para começar, vamos instalar algumas bibliotecas Debian necessárias:
|
||||
```
|
||||
$ sudo apt-get install clang
|
||||
$ sudo apt-get install libcurl4 libpython2.7 libpython2.7-dev
|
||||
```
|
||||
Se estivermos usando o Debian 10 ou superior (e realmente deveríamos estar usando), também precisaremos retroagir algumas bibliotecas para obter as versões mais antigas:
|
||||
```
|
||||
$ sudo apt-get install libtinfo5 libncurses5
|
||||
```
|
||||
Posteriormente, podemos baixar e instalar o Swift:
|
||||
```
|
||||
$ wget https://swift.org/builds/swift-5.1.3-release/ubuntu1804/swift-5.1.3-RELEASE/swift-5.1.3-RELEASE-ubuntu18.04.tar.gz
|
||||
$ tar xzfv swift-5.1.3-RELEASE-ubuntu18.04.tar.gz
|
||||
$ sudo mv swift-5.1.3-RELEASE-ubuntu18.04 /usr/share/swift
|
||||
```
|
||||
Para poder usar nossa nova configuração do Swift, precisaremos atualizar nosso `PATH` em nosso arquivo `.bashrc`:
|
||||
```
|
||||
$ echo "export PATH=/usr/share/swift/usr/bin:$PATH" >> ~/.bashrc
|
||||
$ source ~/.bashrc
|
||||
```
|
||||
Agora podemos testar o Swift com o argumento `--version`:
|
||||
```
|
||||
$ swift --version
|
||||
Swift version 5.1.3 (swift-5.1.3-RELEASE)
|
||||
Target: x86_64-unknown-linux-gnu
|
||||
```
|
||||
|
||||
### Criando um projeto
|
||||
Depois de instalar o Swift em nossa máquina Ubuntu, podemos criar projetos com o comando `package init`:
|
||||
```
|
||||
$ mkdir swift-project
|
||||
$ cd swift-project/
|
||||
/swift-project$ swift package init --type executable
|
||||
Creating executable package: swift-project
|
||||
Creating Package.swift
|
||||
Creating README.md
|
||||
Creating .gitignore
|
||||
Creating Sources/
|
||||
Creating Sources/swift-project/main.swift
|
||||
Creating Tests/
|
||||
Creating Tests/LinuxMain.swift
|
||||
Creating Tests/swift-projectTests/
|
||||
Creating Tests/swift-projectTests/swift_projectTests.swift
|
||||
Creating Tests/swift-projectTests/XCTestManifests.swift
|
||||
```
|
||||
Também editaremos o `Sources/.../main.swift` e quando estivermos pronto para compilar, podemos usar o comando `build`:
|
||||
```
|
||||
$ swift build
|
||||
[4/4] Linking swift-project
|
||||
```
|
||||
Finalmente, seremos capazes de executar o programa a partir do diretório `.build/debug`:
|
||||
```
|
||||
$ .build/debug/swift-project
|
||||
Hello, world!
|
||||
```
|
||||
Boa sorte!
|
Loading…
x
Reference in New Issue
Block a user