3  Prática: Explorando a Ethereum Virtual Machine (EVM)

Visão Geral

Resumo
A proposta desta aula é termos uma ideia sobre o funcionamento da EVM, instalando a ferramenta evm e executando alguns exemplos.

3.1 Introdução

Faça a leitura do Capítulo 13: The Ethereum Virtual Machine

A proposta desta prática é vermos o funcionamento da EVM.

3.2 Instalando o evm

Nesta etapa, você instalará o cliente padrão de camada de execução (geth) e outras ferramentas. Iremos utilizar o evm, que é uma interface por linha de comando para a Ethereum Virtual Machine. Baixe e execute o a última versão 64-bit estável do Geth and Tools para seu Sistema Operacional do site Geth downloads page. Para Linux, baixe o arquivo Geth and Tools ou escolha nas guias a versão do seu Sistema Operacional.

O geth e as outras ferramentas podem ser instaladas em sistemas derivados do Debian e Ubuntu com o pacote ethereum:

$ sudo apt-get install -y software-properties-common
$ sudo add-apt-repository -y ppa:ethereum/ethereum
$ sudo apt-get update
$ sudo apt-get install -y ethereum

Em outros Sistemas como o Manjaro Linux:

$ sudo pacaur -Ss ethereum
$ sudo pacaur -Ss go-ethereum
extra/go-ethereum 1.14.8-1 [instalado]
    Official Go implementation of the Ethereum protocol
$ pacaur -S go-ethereum
atencao: go-ethereum-1.14.8-1 está atualizado -- reinstalando
resolvendo dependencias...
procurando pacotes conflitantes...

Pacotes (1) go-ethereum-1.14.8-1

Tamanho total instalado:  249,48 MiB
Alteracao no tamanho:       0,00 MiB

:: Continuar a instalacao? [S/n]

Imagens docker são mantidas no DockerHub para usuários que preferem rodar em um ambiente com container. As seguintes imagens estão disponíveis com todas as ferramentas:

  • ethereum/client-go:alltools-latest última versão de desenvolvimento.
  • ethereum/client-go:alltools-stable última versão estável.
  • ethereum/client-go:alltools-{version} versão estável com número de versão específica.
  • ethereum/client-go:alltools-release-{version} última versão estável com um família de versões específica.

Instalação via docker pode ser feita com o comando:

$ docker pull ethereum/client-go:alltools-stable
alltools-stable: Pulling from ethereum/client-go
9ad3caf28a48: Pull complete 
9824c27679d3: Pull complete 
b8a14d1aed66: Pull complete 
Digest: sha256:9b722323bf4a6c147f55fb162b78f54b3a074445e658b1259dee088c382cac51
Status: Downloaded newer image for ethereum/client-go:alltools-stable
docker.io/ethereum/client-go:alltools-stable

A execução pode ser feita com o comando docker run e os parâmetros:

$ docker run -it -p 30303:30303 ethereum/client-go:alltools-stable
/ # geth --version
geth version 1.16.2-stable-dd1ebac1
/ # 

A imagem irá expor as seguintes portas:

8545 TCP, usado pela API HTTP baseada em JSON RPC
8546 TCP, usado pela API WebSocket baseada em JSON RPC
8547 TCP, usado pela API GraphQL
30303 TCP e UDP, usado pelo protocolo P2P para rodar a rede.

Nota: se você estiver executando um cliente dentro de um container docker, você deveria montar um volume de dados com um diretório de dados do cliente na pasta /root/.ethereum dentro do container para que ter certeza que os dados baixados serão preservados entre uma reinicialização e outra do container.

Na minha máquina montei o diretório /run/media/rag/DADOS/ethereum no diretório virtual /root/.ethereum, as sincronizações e comandos executados na imagem docker seriam refletidos na base de dados do diretório externo.

$ docker run -it -p 30303:30303 -v /run/media/rag/DADOS/ethereum:/root/.ethereum  -w /root/.ethereum ethereum/client-go:alltools-latest

Para atualizar as ferramentas para a última versão, basta parar o container e fazer um pull da última versão:

$ docker stop ethereum/client-go:alltools-stable
$ docker pull ethereum/client-go:alltools-latest
$ docker run -it -p 30303:30303 ethereum/client-go:alltools-latest

Existem quatro versões diferentes via imagem docker, a última versão de desenvolvimento, a última versão estável ou versões estáveis específicas:

  • ethereum/client-go: mais recente versão de desenvolvimento (padrão).
  • ethereum/client-go: última versão estável.
  • ethereum/client-go:{version}: é a versão estável para um número de versão específico.
  • ethereum/client-go:release-{version}: é a última versão estável do geth para uma versão específica.

A execução da última versão seria pelo comando:

$ docker run -it -p 30303:30303 ethereum/client-go:alltools-latest

Instruções para outros Sistemas Operacionais podem ser encontradas no site oficial da documentação do Ethereum, artigo Installing Geth.

Verifique se a versão mais nova já não foi instalada. O link Sprouted Seed Vial (v1.16.2) lista as versões.

Na minha máquina está instalada a versão 1.16.2-stable-dd1ebac1, você pode verificar a sua com geth --version ou geth version ou evm --version:

$ evm --version
evm version 1.16.2-stable-dd1ebac1
$ geth --version
geth version 1.16.2-stable-dd1ebac1
$ geth version
Geth
Version: 1.16.2-stable
Git Commit: dd1ebac11757484575ee779381af32f29ce3fbe4
Git Commit Date: 20250804
Architecture: amd64
Go Version: go1.24.5
Operating System: linux
GOPATH=
GOROOT=
$ 

Para a execução de uma versão específica como a 1.14.12, versão instalada no laboratório, utilize o comando docker, substituindo em {version} com v1.14.12:

$ docker run -it -p 30303:30303 ethereum/client-go:alltools-v1.14.12

Para que a evm tenha suporte aos opcodes da versão Shangai. Crie um arquivo genesis.json com o conteúdo:

{
  "config": {
    "chainId": 9599,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0,
    "istanbulBlock": 0,
    "berlinBlock": 0,
    "londonBlock": 0,
    "shanghaiBlock": 0,
    "shanghaiTime":0
  },
  "alloc": {
    "0x1c7cd2d37ffd63856a5bd56a9af1643f2bcf545f": {
      "balance": "0xffffffffffffffffffffffffffffffffffffffff"
    }
  },
  "coinbase": "0x000000000000000000000000000000000000abcd",
  "difficulty": "0x0",
  "extraData": "",
  "gasLimit": "0xffffff",
  "nonce": "0x0000000000000042",
  "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp": "0x00"
}

3.3 Atividade 1: Somando dois valores na Pilha da EVM

  1. Crie um arquivo exemplo-001.asm com as instruções:
PUSH 0x01
PUSH 0x02
ADD
STOP

O exemplod e código empilha dos valores \(1\) e \(2\) e faz um ADD, deixando o valor \(3\) no topo da pilha.

  1. Execute o comando:
$ evm --debug --gas 0xffffff --nomemory=false --input 9ceee80c --prestate ./genesis.json run exemplo-001.asm

Será produzido um resultado semelhante a esse:

INFO [08-29|14:46:00.855] Persisted trie from memory database      nodes=1 size=160.00B time="3.352us" gcnodes=0 gcsize=0.00B gctime=0s livenodes=0 livesize=0.00B

#\#\#\# TRACE \#\#\#\#
PUSH1           pc=00000000 gas=16777215 cost=3

PUSH1           pc=00000002 gas=16777212 cost=3
Stack:
00000000  0x1

ADD             pc=00000004 gas=16777209 cost=3
Stack:
00000000  0x2
00000001  0x1

STOP            pc=00000005 gas=16777206 cost=0
Stack:
00000000  0x3

#\#\#\# LOGS \#\#\#\#

Note o conteúdo da Pilha (Stack) sendo modificado com a inserção dos valores \(0x01\) e \(0x02\) e finalizando com a soma \(0x03\).

Uma outra forma de executar é compilando o código:

$ evm compile exemplo-001.asm 
600160020100

E executando o binário passado diretamente por parâmetro na opção --code 600160020100.

$ evm --debug --gas 0xffffff --nomemory=false --input 9ceee80c --prestate ./genesis.json run --code 600160020100

Temos o mesmo resultado:

INFO [08-29|15:17:42.526] Persisted trie from memory database      nodes=1 size=160.00B time="4.26us" gcnodes=0 gcsize=0.00B gctime=0s livenodes=0 livesize=0.00B

#\#\#\# TRACE \#\#\#\#
PUSH1           pc=00000000 gas=16777215 cost=3

PUSH1           pc=00000002 gas=16777212 cost=3
Stack:
00000000  0x1

ADD             pc=00000004 gas=16777209 cost=3
Stack:
00000000  0x2
00000001  0x1

STOP            pc=00000005 gas=16777206 cost=0
Stack:
00000000  0x3

#\#\#\# LOGS \#\#\#\#
$ 

3.4 Atividade 2: Executando código de um contrato

Iremos baixar e executar o código de um contrato. Para isso precisaremos recuperar o bytecode de um contrato. Acesse os detalhes da transação 0x8b42fe608875d106ca3a010766c53a449da92076e 2e46e38861090f508cf1504 no etherscan.io.

Detalhes da Transação

Copie o código binário do contrato (0x353755fa4c3d8e73ca5190d86894866e9bd6abb8) armazenado, conteúdo do campo Input Data para o arquivo transacao-0x8b42fe608875d106ca3a010766c53a449da9207 6e2e46e38861090f508cf1504-contrato-0x353755fa4c3d8e73ca5190d86894866e9bd6abb8.bin.

Execute a evm com o código do contrato 0x353755fa4c3d8e73ca5190d86894866e9bd6abb8 passando o arquivo com o código binário através da opção --codefile.

evm --debug --gas 0xffffff --nomemory=false --input 9ceee80c --prestate ./genesis.json run --codefile transacao-0x8b42fe608875d106ca3a010766c53a449da92076e2e46e38861090f508cf1504-contrato- 0x353755fa4c3d8e73ca5190d86894866e9bd6abb8.bin

Observe as alterações do conteúdo de cada estrutura.

3.5 Atividade 3: Outras Operações

Utilize a referência de comandos da EVM para estender o exemplo-001.asm da Atividade \(1\) com operações de escrita e leitura em memória, no storage e outras operações lógicas e aritméticas.

3.6 Relatório

Escreva um relatório sobre a Prática. Formato: Um arquivo PDF com sua identificação.

3.7 Leitura Recomendada

[alertblock]{Leitura Recomendada}

Capítulo 13: The Ethereum Virtual Machine

Livro: Antonopoulos, A. M., and G. Wood. 2018. Mastering Ethereum: Building Smart Contracts and DApps. O’Reilly Media, Incorporated.

[/alertblock]

3.8 Considerações Finais

3.9 Próximas Aulas

  • Instalando o Cliente Ethereum: Geth.

4 Referências

4.1 Word Cloud

4.2 Referências