# 18.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. Devemos consultar como fazer isso em ["Construindo Python da Fonte"](18_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 não instalamos o `pip`, precisaremos executar o seguinte: ``` $ sudo apt install python3-pip ``` Em seguida, vamos repetir o comando `pip3 install python-bitcoinrpc`. ### Criando 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 e rpc_password estão configurados no arquivo bitcoin.conf 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 `:@:`. 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 com 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/18_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/18_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 o 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] # novamente, selecionando o primeiro utxo aqui 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 # enviando metade das moedas para o recebedor miner_fee = 0.00000300 # escolha uma taxa apropriada baseada no tamanho da sua transação 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 qual 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 criá-la. ```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": , "vout": }]' '{"
": }'``` 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 colocamos 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/18_4_sendtx.py) está cheio de instruções com `print` para demonstrar todos os dados disponíveis a 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 a 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 da API do 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 [§18.5: Acessando o Bitcoind com Rust](18_5_Accessing_Bitcoind_with_Rust.md). ## Variante: Construindo Python da Fonte Se precisarmos instalar o Python 3 a partir do código-fonte, precisaremos seguir estas instruções, e então continuar com ["Criando um projeto BitcoinRPC"](18_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 # coloque o número de núcleos que você quer usar do seu sistema para acelerar o processo $ 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 ```