Back to Blog

Práticas de Segurança no Desenvolvimento em Move (1): Olá Mundo

Code Auditing
November 7, 2022
11 min read

Com o lançamento da mainnet da rede Aptos em 18 de outubro, a linguagem de programação Move e seu ecossistema expandem sua influência continuamente. Para engajar a comunidade, lançaremos uma série de artigos com práticas de desenvolvimento seguro em Move. Vamos guiá-lo ao mundo Move por meio de exemplos, explicações e, mais importante, práticas de segurança.

TL;DR

Este artigo irá te explicar:

  • como criar e desenvolver uma aplicação Aptos;
  • como é um módulo (ou seja, contrato inteligente em Move);
  • como compilar e publicar um módulo na sua rede local;
  • como interagir com o módulo e verificar o evento emitido no navegador.

0x1. Sobre Move e Seu Ecossistema

Move é uma herança do Diem (o nome anterior era Libra antes de dezembro de 2020), projetado para o desenvolvimento de contratos inteligentes. É considerado seguro, rápido e flexível, com o objetivo de se tornar a linguagem de próxima geração. No entanto, projetos recém-introduzidos como Aptos e Sui não adotam o Move original diretamente, sendo que algumas modificações foram feitas para atender às suas necessidades.

Salvo indicação em contrário, vamos nos concentrar principalmente nas práticas de desenvolvimento no contexto do Aptos, já que as ideias são genéricas para outros projetos baseados em Move. Também assumimos que você já leu o MoveBook para uma compreensão básica da linguagem de programação Move.

0x2. Preparar o Ambiente

0x2.1 Cadeia de Ferramentas Aptos

O Aptos já integrou o Move em sua CLI. Portanto, é recomendado seguir a instalação da CLI para instalar a cadeia de ferramentas correspondente. Após concluir a instalação, digite aptos no terminal e você deverá ver a seguinte saída:

$ aptos
aptos 1.0.1
Aptos Labs <[email protected]>
Command Line Interface (CLI) for developing and interacting with the Aptos blockchain
USAGE:
    aptos <SUBCOMMAND>
OPTIONS:
    -h, --help       Print help information
    -V, --version    Print version information
SUBCOMMANDS:
    account       Tool for interacting with accounts
    config        Tool for interacting with configuration of the Aptos CLI tool
    genesis       Tool for setting up an Aptos chain Genesis transaction
    governance    Tool for on-chain governance
    help          Print this message or the help of the given subcommand(s)
    info          Show build information about the CLI
    init          Tool to initialize current directory for the aptos tool
    key           Tool for generating, inspecting, and interacting with keys
    move          Tool for Move related operations
    node          Tool for operations related to nodes
    stake         Tool for manipulating stake and stake pools

0x2.2 Testnet Local e Conta

O Aptos oferece várias redes (ou seja, mainnet, testnet, devnet e testnet local) para desenvolvimento e implantação. Neste artigo, usaremos a testnet local.

Para iniciar a testnet local, digite o seguinte comando:

$ aptos node run-local-testnet --with-faucet --force-restart

Você deverá ver a saída:

......
Aptos is running, press ctrl-c to exit
Faucet is running.  Faucet endpoint: 0.0.0.0:8081

Agora você pode criar sua conta digitando o seguinte comando (provavelmente em outra janela de terminal se quiser manter a anterior em primeiro plano):

$ aptos init

Este comando abrirá um shell interativo, que requer a especificação de algumas propriedades. Aqui escolhemos a rede local. Após isso, haverá uma pasta .aptos no diretório atual com a configuração da sua conta padrão. Verifique o arquivo e copie o endereço da sua account (ex.: c0d8xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx14bd).

Dica#1: Mantenha seu arquivo de configuração de conta seguro! Sua chave privada está escrita nele!

0x3. O Primeiro Programa Hello World

O programa Hello World é sempre usado como primeiro passo no aprendizado de escrita de código. Gostaríamos de seguir essa convenção para demonstrar a forma de desenvolver aplicações Move.

0x3.1 Preparar o Pacote

Escolha um diretório de sua preferência e digite o seguinte comando:

$ aptos move init --framework-local-dir "../../aptos-core/aptos-move/framework/aptos-framework" --name hello

Este comando criará um novo pacote Move no local especificado (ou seja, o espaço de trabalho de desenvolvimento). Como algumas funções de biblioteca do framework Aptos são necessárias, o diretório local do framework Aptos é especificado (o caminho relativo pode precisar ser alterado para um local diferente); alternativamente, a revisão ou branch git do framework Aptos também pode ser especificada usando --framework-git-rev.

Agora você poderá ver um arquivo Move.toml no seu diretório. Este arquivo não apenas lista algumas configurações de dependência, mas também descreve o nome e a versão do pacote. Além disso, também existe uma pasta chamada sources, que é usada para armazenar o código-fonte Move. Após criar um arquivo .move no diretório sources, o espaço de trabalho estará pronto para uso.

[package]
name = 'hello'
version = '1.0.0'
[dependencies.AptosFramework]
local = '../../aptos-core/aptos-move/framework/aptos-framework'
[addresses]
BlockSec="c0d8xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx14bd"

Na última linha do Move.toml, associamos o endereço da conta (recém-criado) ao BlockSec. Ele é usado para publicar o módulo (ou seja, implantar o contrato inteligente) neste pacote (será discutido posteriormente). Observe que o endereço acima é apenas um espaço reservado e você DEVE substituí-lo pelo seu próprio endereço de conta.

0x3.2 Preparar o Módulo

Abaixo está um módulo de exemplo simples, mas completo, que pode ser colocado diretamente no arquivo move (ex.: hello.move). A seguir, gostaríamos de percorrer o código para ilustrar a diferença entre Move e outras linguagens de programação. Você pode pular esta seção se já estiver bem familiarizado com elas.

module BlockSec::hello{
    use aptos_framework::account;
    use aptos_framework::event;
    use std::signer;
    use std::string::{String, utf8};
    struct SecEventStore has key{
        event_handle: event::EventHandle<String>,
    }
    public entry fun say_hello_script(account: &signer) acquires SecEventStore{
        say_hello(account);
    }
    public fun say_hello(account: &signer) acquires SecEventStore{
        let address_ = signer::address_of(account);
        if(!exists<SecEventStore>(address_)){
            move_to(account, SecEventStore{event_handle: account::new_event_handle(account)});
        };
        event::emit_event<String>(&mut borrow_global_mut<SecEventStore>(address_).event_handle, utf8(b"Hello World!"));
    }
}

Aqui definimos um módulo chamado hello que será publicado no endereço BlockSec. A função de entrada deste módulo é chamada say_hello_script, que invocará a função say_function. É fácil perceber que este módulo é usado para emitir um evento com a mensagem "Hello World!" (na função say_hello). No entanto, ainda existem alguns detalhes que precisam ser claramente explicados.

Especificamente, para usar a função event::emit_event no namespace aptos_framework, precisamos de uma struct event::EventHandle porque a função emit_event tem a seguinte declaração:

/// Emit an event with payload `msg` by using `handle_ref`'s key and counter.
public fun emit_event<T: drop + store>(handle_ref: &mut EventHandle<T>, msg: T)
struct EventHandle<phantom T: drop + store> has store {
    /// Total number of events emitted to this event stream.
    counter: u64,
    /// A globally unique ID for this event stream.
    guid: GUID,
}

Observe que EventHandle não possui a habilidade key, o que significa que precisamos armazená-la em outra struct. Portanto, definimos uma struct chamada SecEventStore para armazenar o EventHandle:

struct SecEventStore has key{
    event_handle: event::EventHandle<String>,
}

Dica#2: Devemos considerar as habilidades de uma struct em Move. Lembre-se de que apenas a habilidade **_key_** pode ser usada com **_move_to_**. Se um módulo retorna uma struct que possui apenas a habilidade **_store_** e você deseja preservá-la no armazenamento global, considere definir uma nova struct como invólucro.

Por simplicidade, usamos String para representar a mensagem. Portanto, o passo final da função say_hello é:

event::emit_event<String>(&mut borrow_global_mut<SecEventStore>(address_).event_handle, utf8(b"Hello World!"));

Dica#3: Move não suporta o tipo **_String_** nativamente. A biblioteca padrão fornece a capacidade de converter um vetor de bytes em uma **_String_**. Consulte o código-fonte no repositório GitHub deles para mais detalhes.

Para garantir que todo novo usuário se registre com um SecEventStore, precisamos primeiro verificar a existência da struct na conta do usuário por meio da função exists. Se o usuário não possuir o recurso, a função criará um e o moverá para a conta do usuário.

if(!exists<SecEventStore>(address_)){
    move_to(account, SecEventStore{event_handle: account::new_event_handle(account)});
};

0x3.3 Compilar e Publicar o Módulo

Quando o módulo estiver pronto, podemos compilá-lo com o seguinte comando:

$ aptos move compile
Compiling, may take a little while to download git dependencies...
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING hello
{
  "Result": [
    "c0d8xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx14bd::hello"
  ]
}

Também podemos testá-lo se necessário:

$ aptos move test
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING hello
Running Move unit tests
Test result: OK. Total tests: 0; passed: 0; failed: 0
{
  "Result": "Success"
}

Agora podemos implantar o módulo na rede Aptos. Lembre-se de que associamos a conta ao BlockSec, pois um usuário só pode publicar módulos em contas sob seu controle. Além disso, o faucet já financiou a conta com alguns APTs para o pagamento de taxas de gás. A única coisa restante é publicar o módulo no pacote Move na rede Aptos, como segue:

$ aptos move publish --package-dir ./ --profile default
Compiling, may take a little while to download git dependencies...
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING hello
package size 1271 bytes
Do you want to submit a transaction for a range of [672200 - 1008300] Octas at a gas unit price of 100 Octas? [yes/no] >
yes
{
  "Result":
  ...
}

Novamente, também é um shell interativo, e o conteúdo do Result foi omitido.

0x3.4 Interagir com o Módulo

Para fazer o módulo emitir o evento, basta digitar o seguinte comando:

$ aptos move run --function-id default::hello::say_hello_script
Do you want to submit a transaction for a range of [34500 - 51700] Octas at a gas unit price of 100 Octas? [yes/no] >
yes
{
  "Result": {
    "transaction_hash": "0x9af16532de5e79803c823fe28e3251703927d93809274b76972d8e83c6fcd433",
    ...
    }
}

Como função de entrada, a função say_hello_script pode ser invocada diretamente. Você também pode escrever um script e invocar a função say_hello no seu script.

Dica#4: Para uma melhor experiência de interação, definir algumas funções de entrada é uma boa prática. Você também pode desenvolver alguns scripts para os usuários interagirem com seu projeto.

Ao contrário do programa Hello World tradicional da Web2, o evento emitido não será exibido no shell. Acesse o explorador Aptos e selecione a rede local.

Em seguida, copie o valor de transaction_hash do shell e cole-o na barra de pesquisa, e você verá os detalhes da transação.

Por fim, clique em Events, e a rede Aptos expressará suas saudações a você.

0x4. O Que Vem a Seguir?

O programa Hello World é apenas um pequeno passo para o desenvolvimento de aplicações Move. Na série de artigos que virá a seguir, apresentaremos mais sobre o desenvolvimento de aplicações Move seguras no Aptos. Fique ligado!

Sobre a BlockSec

A BlockSec é uma empresa pioneira em segurança blockchain, fundada em 2021 por um grupo de especialistas em segurança mundialmente reconhecidos. A empresa está comprometida em aprimorar a segurança e a usabilidade para o emergente mundo Web3, a fim de facilitar sua adoção em massa. Para isso, a BlockSec oferece serviços de auditoria de segurança de contratos inteligentes e chains EVM, a plataforma Phalcon para desenvolvimento de segurança e bloqueio proativo de ameaças, a plataforma MetaSleuth para rastreamento e investigação de fundos, e a extensão MetaSuites para construtores web3 navegarem com eficiência no mundo cripto.

Até o momento, a empresa atendeu mais de 300 clientes ilustres, como MetaMask, Uniswap Foundation, Compound, Forta e PancakeSwap, e recebeu dezenas de milhões de dólares americanos em duas rodadas de financiamento de investidores proeminentes, incluindo Matrix Partners, Vitalbridge Capital e Fenbushi Capital.

Site oficial: https://blocksec.com/

Conta oficial no Twitter: https://twitter.com/BlockSecTeam

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit