Pular para o conteúdo principal

Uma introdução do desenvolvedor Python ao Ethereum, parte 1

pythonweb3.py
Iniciante
Marc Garreau
O encantador de cobras(opens in a new tab)
8 de setembro de 2020
12 minutos de leitura minute read

Então, você ouviu falar sobre esta coisa do Ethereum e está pronto para se aventurar no buraco do coelho? Este post cobrirá rapidamente alguns conceitos básicos de blockchain e, em seguida, fará com que você interaja com um nó simulado do Ethereum – lendo dados do bloco, verificando saldos da conta e enviando transações. Em todo o caminho, vamos destacar as diferenças entre as maneiras tradicionais de desenvolver aplicativos e este novo paradigma descentralizado.

Pré-requisitos de software

Este post espera ser acessível a muitos desenvolvedores. Usaremos ferramentas Python, mas elas são apenas um meio para as ideias — não há problema se você não é um desenvolvedor Python. No entanto, vou fazer apenas algumas suposições sobre o que você já sabe, para que possamos passar rapidamente aos temas específicos do Ethereum.

Suposições:

  • Você pode utilizar um terminal,
  • Você já escreveu algumas linhas de código Python,
  • A versão 3.6 ou superior do Python está instalada no seu computador (uso de um ambiente virtual(opens in a new tab) é fortemente recomendado) e
  • você já usou o pip, instalador de pacotes do Python. Novamente, se algum destes pré-requisitos não for verdadeiro, ou se você não planeja reproduzir o código apresentado neste artigo, é provável que você ainda possa acompanhar sem maiores problemas.

Blockchains, em poucas palavras

Há muitas maneiras de descrever o Ethereum, mas no fundo é uma blockchain. As blockchains são compostas de uma série de blocos, então vamos começar por aí. Em termos mais simples, cada bloco na blockchain do Ethereum é somente um conjunto de metadados e uma lista de transações. No formato JSON, que se parece com algo assim:

1{
2 "number": 1234567,
3 "hash": "0xabc123...",
4 "parentHash": "0xdef456...",
5 ...,
6 "transactions": [...]
7}
Copiar

Cada bloco tem uma referência ao bloco que veio antes dele; o parentHash é simplesmente o hash do bloco anterior.

Observação: o Ethereum faz uso regular de funções hash(opens in a new tab) para produzir valores de tamanho fixo ("hashes"). Os hashes desempenham um papel importante no Ethereum, mas você pode pensar neles como IDs exclusivos por enquanto.

Um diagrama que retrata uma blockchain incluindo os dados dentro de cada bloco

Uma blockchain é essencialmente uma lista encadeada; onde cada bloco tem uma referência ao bloco anterior.

Esta estrutura de dados não é nada nova, mas as regras (por exemplo, os protocolos peer-to-peer) que regem a rede são. Não há autoridade central; a rede de pares deve colaborar para sustentar a rede e competir para decidir quais transações incluir no próximo bloco. Então, quando você quiser enviar algum dinheiro para um amigo, você precisará transmitir essa transação para a rede, depois espere que ela seja incluída em um próximo bloco.

A única maneira de a blockchain verificar se o dinheiro foi realmente enviado de um usuário para outro é usar uma moeda nativa de (isto é, criada e governada por) aquela blockchain. No Ethereum, esta moeda é chamada ether, e a blockchain Ethereum contém o único registro oficial dos saldos das contas.

Um novo paradigma

Esta nova pilha de tecnologia descentralizada gerou novas ferramentas de desenvolvedor. Tais ferramentas existem em muitas linguagens de programação, mas vamos analisar através das lentes do Python. Para reiterar: mesmo que o Python não seja sua linguagem escolhida, você não terá muitos problemas para acompanhar.

Os desenvolvedores do Python que querem interagir com o Ethereum provavelmente usem a Web3.py(opens in a new tab). Web3.py é uma biblioteca que simplifica muito a forma como você se conecta a um nó Ethereum, e depois envia e recebe dados dele.

Nota: "Nó Ethereum" e "Cliente Ethereum" são usados de forma intercambiável. Em ambos os casos, refere-se ao software que um participante da rede Ethereum executa. Este software pode ler dados de blocos, receber atualizações quando novos blocos são adicionados à cadeia, transmitir novas transações e mais. Tecnicamente, o cliente é o software, o nódulo é o computador que executa o software.

Clientes Ethereum podem ser configurados para serem acessíveis por IPC(opens in a new tab), HTTPS ou Websockets, então Web3. y precisará espelhar esta configuração. Web3.py refere-se a estas opções de conexão como provedores. Você vai querer escolher um dos três provedores para vincular a instância da Web3.py ao seu nó.

Um diagrama mostrando como web3.py usa IPC para conectar seu aplicativo a um nódulo Ethereum

Configure o nó Ethereum e o Web3.py para se comunicarem através do mesmo protocolo, por exemplo, o IPC neste diagrama.

Uma vez que o Web3.py estiver configurado corretamente, você pode começar a interagir com a blockchain. Aqui estão alguns exemplos de uso da Web3.py como uma prévia do que está por vir:

1# read block data:
2w3.eth.get_block('latest')
3
4# send a transaction:
5w3.eth.send_transaction({'from': ..., 'to': ..., 'value': ...})
Copiar

Instalação

Neste guia, vamos trabalhar apenas com um interpretador de Python. Não criaremos nenhum diretório, arquivos, classes ou funções.

Observação: nos exemplos abaixo, os comandos que começam com `$` devem ser executados no terminal. (Não digite o `$`, ele apenas significa o início da linha.)

Primeiro, instale IPython(opens in a new tab) para explorar em um ambiente amigável. IPython propõe, entre outros, um recurso de autopreenchimento com tab, o que facilita a navegação no Web3.py.

pip install ipython

Web3.py é publicado sob o nome web3. Instale-o assim:

pip install web3

Mais uma coisa: vamos simular uma blockchain mais tarde, o que requer mais algumas dependências. Você pode instalar por meio de:

pip install 'web3[tester]'

Você está pronto para começar!

Nota: o pacote web3[tester] funciona até Python 3.10.xx

Crie uma sandbox

Abra um novo ambiente Python executando o ipython no seu terminal. Isso é como executar python, mas vem com mais "efeitos especiais".

ipython

Isso mostrará algumas informações sobre as versões do Python e do IPython que você está executando, então você deve ver um prompt esperando por uma entrada:

1In [1]:
Copiar

Você está olhando para um shell interativo do Python agora. Essencialmente, é um ambiente de testes para brincar. Se você chegou até aqui, é hora de importar Web3.py:

1In [1]: from web3 import Web3
Copiar

Introduzindo o módulo Web3

Além de ser um gateway para Ethereum, o módulo Web3(opens in a new tab) oferece algumas funções práticas. Vamos ver algumas.

Em um aplicativo Ethereum, você normalmente precisará converter denominações de moeda. O módulo web3 fornece alguns métodos auxiliares apenas para isso: from_wei(opens in a new tab) e to_wei(opens in a new tab).

Observação: os computadores são notoriamente pouco eficazes para lidar com números decimais. Para contornar isso, os desenvolvedores costumam armazenar valores em dólares em centavos. Por exemplo, um item com preço de $5,99 pode ser armazenado no banco de dados como 599.

Um padrão similar é usado ao lidar com transações em ether. No entanto, em vez de dois pontos decimais, ether tem 18! A menor denominação de ether é chamada de wei, portanto, esse é o valor especificado ao enviar transações.

1 ether = 1000000000000000000 wei

1 wei = 0,000000000000000001 ether

Tente converter alguns valores de e para wei. Note que existem nomes para muitas das denominações(opens in a new tab) entre ether e wei. Um dos mais conhecidos entre eles é o gwei, já que é frequentemente como as taxas de transação são representadas.

1In [2]: Web3.to_wei(1, 'ether')
2Out[2]: 1000000000000000000
3
4In [3]: Web3.from_wei(500000000, 'gwei')
5Out[3]: Decimal('0.5')
Copiar

Outros métodos utilitários no módulo Web3 incluem conversores de formato de dados (por exemplo, toHex(opens in a new tab)), os métodos para gerar endereços (por exemplo, isAddress(opens in a new tab)) e funções hash (por exemplo, keccak(opens in a new tab)). Muitos deles serão cobertos mais tarde pela série. Para ver todos os métodos e propriedades disponíveis, utilize o autopreenchimento do IPython, digitando Web3 e pressionando a tecla tab duas vezes após o ponto.

Se comunicar com a cadeia

Os métodos práticos são convenientes, mas vamos passar à blockchain. O próximo passo é configurar o Web3.py para se comunicar com um nó Ethereum. Aqui temos a opção de usar os provedores IPC, HTTPS ou Websocket.

Não vamos nos adentrar nesse tema, mas um exemplo de um fluxo de trabalho completo usando o provedor HTTP pode parecer algo assim:

  • Baixe um nó Ethereum, por exemplo, Geth(opens in a new tab).
  • Inicie o Geth em uma janela de terminal e aguarde a sincronização da rede. A porta HTTP padrão é 8545, mas é configurável.
  • Peça ao Web3.py para se conectar ao nó via HTTP, no localhost:8545. w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))
  • Use a instância w3 para interagir com o nó.

Embora esta seja uma maneira "real" de fazer isso, o processo de sincronização leva horas e é desnecessário se você só quer um ambiente de desenvolvimento. A Web3.py expõe um quarto provedor para esse propósito, o EthereumTesterProvider. Este provedor de teste se vincula a um nó Ethereum simulado com autorizações abrangentes e moeda falsa para utilizar.

Um diagrama mostrando o EthereumTesterProvider que vincula o seu aplicativo web3.py a um nó simulado de Ethereum

O EthereumTesterProvider conecta-se a um nó simulado e é útil para ambientes de desenvolvimento rápido.

O nó simulado é chamado eth-tester(opens in a new tab) e o instalamos como parte do pip install web3[tester]. Configurar o Web3.py para usar este provedor de teste é tão simples como:

1In [4]: w3 = Web3(Web3.EthereumTesterProvider())
Copiar

Agora você está pronto para navegar pela cadeia! Disso as pessoas não falam. É algo que eu acabei de inventar. Façamos um tour rápido.

O tour rápido

Primeiro, uma verificação:

1In [5]: w3.is_connected()
2Out[5]: True
Copiar

Já que estamos utilizando o provedor de teste, esse não é um teste muito importante, mas se ele falhar, você provavelmente digitou algo errado ao instanciar a variável w3. Verifique se você incluiu os parênteses internos, ou seja, Web3.EthereumTesterProvider().

Parada n.º 1 do tour: contas

Por conveniência, o provedor de teste criou algumas contas e as pré-carregou com o ether.

Primeiro, vamos ver uma lista dessas contas:

1In [6]: w3.eth.accounts
2Out[6]: ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
3 '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF',
4 '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69', ...]
Copiar

Se você executar esse comando, você deverá ver uma lista de dez variáveis que começam com 0x. Cada um é um endereço público e é, em alguns aspectos, análogo ao número da conta em uma conta corrente. Você forneceria este endereço a alguém que quisesse mandar ether para você.

Como mencionado, o provedor de teste pré-carregou cada uma dessas contas com algum ether. Vamos descobrir quanto há na primeira conta:

1In [7]: w3.eth.get_balance(w3.eth.accounts[0])
2Out[7]: 1000000000000000000000000
Copiar

São muitos zeros! Antes de você ir rindo até o banco falso, lembre-se daquela lição anterior sobre denominações de moeda. Os valores de ether são representados na menor denominação, wei. Converta para ether:

1In [8]: w3.from_wei(1000000000000000000000000, 'ether')
2Out[8]: Decimal('1000000')
Copiar

Um milhão de ether de teste — nada mal.

Parada n.º 2 do tour: dados do bloco

Vamos dar uma olhada no estado desta blockchain simulada:

1In [9]: w3.eth.get_block('latest')
2Out[9]: AttributeDict({
3 'number': 0,
4 'hash': HexBytes('0x9469878...'),
5 'parentHash': HexBytes('0x0000000...'),
6 ...
7 'transactions': []
8})
Copiar

Sãp fornecidas muitas informações sobre um bloco, mas há apenas alguns pontos para salientar aqui:

  • O número de bloco é zero — não importa quanto tempo você configurou o provedor de teste. Ao contrário da rede Ethereum real, que minera um novo bloco aproximadamente a cada 12 segundos, essa simulação esperará até que você a instrua a fazer alguma coisa.
  • transactions é uma lista vazia, pelo mesmo motivo: ainda não fizemos nada. Este primeiro bloco é um bloco vazio, apenas para iniciar a cadeia.
  • Observe que o parentHash é apenas um monte de bytes vazios. Isso significa que ele é o primeiro bloco da cadeia, também conhecido como bloco de início.

Parada n.º 2 do tour: transações

Estamos parados no bloco zero até que haja uma transação para minerar, então vamos começar. Envie um teste com ether de uma conta para outra:

1In [10]: tx_hash = w3.eth.send_transaction({
2 'from': w3.eth.accounts[0],
3 'to': w3.eth.accounts[1],
4 'value': w3.to_wei(3, 'ether'),
5 'gas': 21000
6})
Copiar

Esse é normalmente o ponto em que você espera (vários segundos) até que sua transação seja minerada em um novo bloco. O processo completo envolve algo como isto:

  1. Envie uma transação e espere pelo hash da transação. Até que o bloco que contém a transação seja criado e transmitido, a transação fica "pendente" tx_hash = w3.eth.send_transaction({ … })
  2. Aguarde a mineração da transação: w3.eth.wait_for_transaction_receipt(tx_hash)
  3. Continue a lógica do aplicativo. Para visualizar a transação bem-sucedida: w3.eth.get_transaction(tx_hash)

Nosso ambiente simulado adicionará a transação a um novo bloco instantaneamente para que possamos ver a transação imediatamente:

1In [11]: w3.eth.get_transaction(tx_hash)
2Out[11]: AttributeDict({
3 'hash': HexBytes('0x15e9fb95dc39...'),
4 'blockNumber': 1,
5 'transactionIndex': 0,
6 'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
7 'to': '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF',
8 'value': 3000000000000000000,
9 ...
10})
Exibir tudo
Copiar

Você verá alguns detalhes familiares aqui: os campos de, para e valor devem corresponder às entradas da chamada sendTransaction. A outra parte tranquilizadora é que esta transação foi incluída como a primeira transação ('transactionIndex': 0) dentro do bloco número 1.

Também podemos ver facilmente o sucesso dessa transação, verificando o saldo das duas contas envolvidas. Três ether deveriam ter sido enviados de uma conta para outra.

1In [12]: w3.eth.get_balance(w3.eth.accounts[0])
2Out[12]: 999996999979000000000000
3
4In [13]: w3.eth.get_balance(w3.eth.accounts[1])
5Out[13]: 1000003000000000000000000
Copiar

O último parece bem! O saldo foi de 1.000.000 a 1.000.003 ether. Mas o que aconteceu com a primeira conta? Parece ter perdido um pouco mais que três ether. Infelizmente, nada na vida é gratuito, e o uso da rede pública Ethereum requer que você compense os seus pares pelo papel de apoio deles. Uma pequena taxa de transação foi deduzida da conta que submeteu a transação - esta taxa é a quantidade de gás queimado (21000 unidades de gás para uma transferência ETH) multiplicado por uma taxa base, que varia de acordo com a atividade da rede, mais a gorjeta que vai para o validador que inclui a transação em um bloco.

Mais sobre gás

Observação: na rede pública, as taxas de transação são variáveis baseadas na demanda da rede e na rapidez com que você gostaria que uma transação fosse processada. Se você estiver interessado em ver como as taxas são calculadas, veja minha publicação anterior sobre como transações são incluídas em um bloco(opens in a new tab).

Agora uma pausa

Já estamos nisso por um tempo, então este parece um lugar tão bom quanto qualquer um para fazer uma pausa. A descoberta continua, e vamos continuar com a parte dois desta série. Alguns conceitos que surgem: conectar a um nó real, contratos inteligentes e tokens. Tem perguntas adicionais? Queremos saber! Seu feedback influenciará o caminho a seguir. Suas perguntas são bem-vindas no Twitter(opens in a new tab).

Última edição: @gabriel.silva(opens in a new tab), 13 de fevereiro de 2024

Este tutorial foi útil?