Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust State Machine: translation section 4 #131

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ Você conseguiu - incrível! Bem-vindo ao tutorial de máquina de estados em Rus
Aqui é Dani (@danicuki) e Yan (@nomadbitcoin) e seremos seus instrutores.
Este projeto é para desenvolvedores que desejam se aprofundar mais na Stack Polkadot baseada em Substrate e na linguagem de programação Rust.

Este tutorial é destinado a ensinar os conceitos básicos de Rust, Blockchain e, eventualmente, o funcionamento interno do [Polkadot SDK](https://github.com/paritytech/polkadot-sdk).
Esta jornada de aprendizado criada pelo WEB3DEV foi baseada no projeto **Rust State Machine de Shawn Tabrizi**, que merece nosso imenso reconhecimento por seu belo trabalho e obrigado por compartilhar seu vasto conhecimento conosco.

O objetivo deste tutorial é ensinar aos desenvolvedores os conceitos básicos de Rust, Blockchain e, eventualmente, o funcionamento interno do [Polkadot SDK](https://github.com/paritytech/polkadot-sdk).

Pela minha experiência, a parte mais difícil de construir seu primeiro blockchain usando o Polkadot SDK é navegar pelos recursos avançados do Rust usados pelo [Substrate](https://github.com/paritytech/polkadot-sdk/tree/master/substrate) e entender a magia por trás de vários macros que geram código para você.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
Antes de prosseguir, verifique se sua resposta corresponde à [solução da etapa anterior aqui](https://gist.github.com/nomadbitcoin/2b3beb37b376732e6c000053e04f07ff).

Incrível! Você dominou outra etapa importante e já está na metade do caminho. Você está prestes a adicionar uma conquista notável ao seu portfólio!

# Executando Blocos e Despachando Chamadas

Nesta próxima seção, você construirá o pipeline central usado para interagir com sua máquina de estados.

Vamos criar a estrutura do bloco que contém as transações para sua função de transição de estado e, em seguida, o pipeline de despacho de função para direcionar essas transações para as chamadas de função apropriadas.

O objetivo desta seção é fazer com que sua máquina de estados existente se pareça com uma blockchain que pode ser estendida e atualizada.

# Adicionando Nosso Módulo de Suporte

[Youtube](https://youtu.be/F9uPCvAX9_k?si=9uGfJAVAEBbq2ewv)

Neste passo, introduziremos um módulo de `support` para ajudar a trazer vários tipos e traits que usaremos para aprimorar nossa máquina de estados simples.

## Construindo um Bloco

O primeiro conjunto de primitivas fornecidas pelo módulo `support` são um conjunto de structs que precisamos para construir um `Block` simples.

#### O Bloco

Um bloco é basicamente dividido em duas partes: o cabeçalho e um vetor de extrínsecos.

Você pode ver que mantemos o `Block` completamente genérico em relação aos tipos `Header` e `Extrinsic`. O conteúdo e as definições exatas desses subtipos podem mudar, mas o struct genérico `Block` pode sempre ser usado.

#### O Cabeçalho

O cabeçalho do bloco contém metadados sobre o bloco, que são usados para verificar se o bloco é válido. Em nossa máquina de estados simples, armazenamos apenas o número do bloco no cabeçalho, mas blockchains reais, como o Polkadot, possuem:

- Hash do bloco pai
- Número do bloco
- Raiz do estado
- Raiz dos extrínsecos
- Digests / Logs de consenso

#### O Extrínseco

Em nossa máquina de estados simples, um extrínseco é sinônimo de transações de usuário.

Assim, nosso tipo extrínseco é composto por uma `Call` (a função que executaremos) e um `Caller` (a conta que deseja executar essa função).

O SDK do Polkadot suporta outros tipos de extrínsecos além de transações de usuário, razão pela qual ele é chamado de `Extrinsic`, mas isso está além do escopo deste tutorial.

### Despachando Chamadas

A próxima mudança importante que faremos em nossa máquina de estados simples é lidar com o despacho de funções. Basicamente, você pode imaginar que poderia haver vários pallets diferentes em seu sistema, cada um com chamadas diferentes que desejam expor.

Seu runtime, atuando como um único ponto de entrada para toda a sua função de transição de estado, precisa ser capaz de direcionar as chamadas recebidas para as funções apropriadas. Para isso, precisamos da trait `Dispatchable`.

### Resultado de Despacho

Uma última coisa que adicionamos ao módulo de suporte foi uma definição simples do tipo `Result` que queremos que todas as chamadas despacháveis retornem. Este é exatamente o tipo que já usamos para a função `fn transfer` e permite que retornemos `Ok(())` se tudo correu bem ou `Err("alguma mensagem de erro")` se algo deu errado.

## Exercícios:

### Crie o Módulo de Suporte

Agora que você entende o que está no módulo de suporte, adicione-o ao seu projeto.

1. Crie o arquivo `support.rs`:

```bash
touch src/support.rs
```

2. Copie e cole o conteúdo fornecido no seu arquivo.
3. Importe o módulo de suporte no topo do seu arquivo `main.rs`.
4. Finalmente, substitua `Result<(), &'static str>` por `crate::support::DispatchResult` na função `fn transfer` no seu Pallet de Saldos.

Crie o arquivo `src/support.rs`:

```rust
/// A representação mais primitiva de um bloco de blockchain.
pub struct Block<Header, Extrinsic> {
/// O cabeçalho do bloco contém metadados sobre o bloco.
pub header: Header,
/// Os extrínsecos representam as transições de estado a serem executadas neste bloco.
pub extrinsics: Vec<Extrinsic>,
}

/// Estamos usando um cabeçalho extremamente simplificado que contém apenas o número atual do bloco.
/// Em uma blockchain real, você esperaria encontrar também:
/// - hash do bloco pai
/// - raiz do estado
/// - raiz dos extrínsecos
/// - etc...
pub struct Header<BlockNumber> {
pub block_number: BlockNumber,
}

/// Este é um "extrínseco": literalmente uma mensagem externa de fora da blockchain.
/// Esta versão simplificada de um extrínseco nos diz quem está fazendo a chamada e qual chamada eles estão fazendo.
pub struct Extrinsic<Caller, Call> {
pub caller: Caller,
pub call: Call,
}

/// O tipo Result para nosso runtime. Quando tudo é concluído com sucesso, retornamos `Ok(())`,
/// caso contrário, retornamos uma mensagem de erro estática.
pub type DispatchResult = Result<(), &'static str>;

/// Uma trait que nos permite despachar um extrínseco recebido para a chamada de função de transição de estado apropriada.
pub trait Dispatch {
/// O tipo usado para identificar o chamador da função.
type Caller;
/// A chamada de função de transição de estado que o chamador está tentando acessar.
type Call;

/// Esta função recebe um `caller` e a `call` que eles querem fazer, e retorna um `Result`
/// com base no resultado dessa chamada de função.
fn dispatch(&mut self, caller: Self::Caller, call: Self::Call) -> DispatchResult;
}
```

No `main.rs`:

```rust
mod balances;
/* TODO: Adicione o módulo de suporte aqui. */
mod system;
```
49 changes: 49 additions & 0 deletions Rust_State_Machine/pt-BR/Section_4/Lesson_2_Block_Type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
Você pode encontrar a [solução para a etapa anterior aqui](https://gist.github.com/nomadbitcoin/a6964532490ce5fbda97a606c07e6f56).

# Criar Seu Tipo de Bloco

[Youtube](https://youtu.be/0ohriBEmJks?si=U8AjrqPhtOosNuwM)

O módulo de suporte forneceu um monte de tipos genéricos que podem ser personalizados para nossa máquina de estados simples. Para realmente começar a usá-los, precisamos definir versões concretas desses tipos usando nossos outros tipos concretos.

## Chamada do Runtime

Você verá que o template fornece um `enum RuntimeCall` vazio que expandiremos mais tarde. Este é um objeto que supostamente representa todas as várias chamadas expostas pela sua blockchain para os usuários e o mundo exterior. Precisamos simular este enum nesta etapa para que ele possa ser usado para construir um tipo `Extrinsic` concreto.

Por enquanto, há apenas a função `transfer` exposta pelo Pallet de Saldos, mas adicionaremos mais antes que este tutorial seja concluído e encontraremos maneiras de automatizar a criação do nosso `RuntimeCall`.

Você pode acessar este tipo dentro de `mod types` com `crate::RuntimeCall`.

## Exercícios:

### Construindo o Tipo de Bloco

É hora de definir o tipo `Block` concreto que usaremos para aprimorar nossa máquina de estados simples.

1. Usando o enum `RuntimeCall` e o tipo `AccountId`, você pode definir um tipo `Extrinsic` concreto.
2. Usando o tipo `BlockNumber`, você pode definir um tipo `Header` concreto.
3. Usando os tipos concretos `Header` e `Extrinsic`, você pode definir um tipo `Block` concreto.

Como você pode ver, o `Block` é composto por camadas de tipos genéricos, permitindo que toda a estrutura seja flexível e personalizável às nossas necessidades.

Preste atenção às definições de tipos genéricos para garantir que você use todos os parâmetros genéricos corretos em todos os lugares certos.

No `main.rs`:

```rust
mod types {
pub type AccountId = String;
pub type Balance = u128;
pub type BlockNumber = u32;
pub type Nonce = u32;
/* TODO: Defina um tipo `Extrinsic` concreto usando `AccountId` e `RuntimeCall`. */
/* TODO: Defina um tipo concreto de `Header` usando `BlockNumber`. */
/* TODO: Defina um tipo concreto de `Block` usando `Header` e `Extrinsic`. */
}

// Estas são todas as chamadas que estão expostas ao mundo.
// Observe que é apenas um acúmulo das chamadas expostas por cada módulo.
pub enum RuntimeCall {
// TODO: Ainda não implementado.
}
```
159 changes: 159 additions & 0 deletions Rust_State_Machine/pt-BR/Section_4/Lesson_3_Executing_Blocks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
Você pode encontrar a [solução para a etapa anterior aqui](https://gist.github.com/nomadbitcoin/5ec6d06dbbb5504db697fb2e2562cb9f).

# Executando Blocos

Agora começaremos o processo de substituir a simulação simples de bloco em nossa função `main` por um pipeline de execução de bloco adequado.

## Executar Bloco

[Youtube](https://youtu.be/B-Q_QMK4Ins?si=QPJqYGuPiYnEVxFs)

Introduzimos uma nova função no nosso `Runtime` chamada `fn execute_block`.

Os passos desta função são exatamente os mesmos da nossa função `main` atual, mas usando o tipo de `Block` concreto que definimos para extrair detalhes como o número do bloco esperado e os extrínsecos que queremos executar.

### Iterando Sobre um Vetor

Para construir nossa função `execute_block`, precisaremos iterar sobre todos os extrínsecos em nosso bloco e despachar essas chamadas. Em Rust, a maneira comum de acessar os elementos de um vetor é transformá-lo em um iterador.

Existem duas funções usadas para transformar um vetor em um iterador, `iter` e `into_iter`, e sua diferença está na propriedade:

- `iter`: Este método cria um iterador que empresta cada elemento do vetor, permitindo que você leia os valores sem tomar posse. É útil quando você quer iterar sobre o vetor mantendo-o intacto.

- `into_iter`: Este método consome o vetor, transferindo a propriedade de cada elemento para o iterador. É útil quando você quer mover ou transferir a propriedade dos elementos do vetor para outra parte do seu código. Após usar `into_iter`, o vetor original não pode mais ser usado, pois a propriedade foi transferida.

No nosso contexto, queremos usar `into_iter()`, então obteremos algo que se parece com:

```rust
for support::Extrinsic { caller, call } in block.extrinsics.into_iter() {
// fazer algo com `caller` e `call`
}
```

Aqui você pode ver que também fazemos um truque para separar os campos do `Extrinsic` em uma única linha, já que, em última análise, queremos trabalhar com `caller` e `call`. Você pode, é claro, dividir esse processo em várias linhas se quiser.

### Despachando uma Chamada

Uma vez que tenhamos o `call` e o `caller`, o que devemos fazer com eles?

É aqui que a trait `Dispatch` começa a entrar em jogo. Você verá em nosso template, incluímos a estrutura de um `unimplemented()` `fn dispatch`. Escreveremos essa lógica no próximo passo, mas precisamos já usar a função `dispatch` em nossa lógica de `execute_block`.

Uma vez que tenhamos o `call` e o `caller`, queremos passá-los para a lógica de `dispatch`, que você vê que está implementada no `Runtime`.

Isso ficará algo assim:

```rust
let _res = self.dispatch(caller, call).map_err(|e| eprintln!("{}", e));
```

Note que em Rust, se você quiser acessar uma função dentro de uma trait, como fazemos aqui com `dispatch`, você precisa importar explicitamente essa trait em seu projeto.

Deixamos um `TODO` no topo de `main.rs` onde pedimos para você importar `crate::support::Dispatch`, que permitirá acessar a chamada `dispatch` no `Runtime`.

### Mensagens de Erro Melhores

Como esta é uma função mais permanente do nosso projeto, também faz sentido expandir a mensagem sendo impressa quando houver erros de extrínsecos. Por exemplo:

```rust
eprintln!(
"Erro de Extrínseco\n\tNúmero do Bloco: {}\n\tNúmero do Extrínseco: {}\n\tErro: {}",
block.header.block_number, i, e
)
```

Isso permite que você veja o número do bloco, o número do extrínseco e a mensagem de erro sempre que houver um erro de extrínseco. Isso pode ser muito útil quando você tem muitos blocos sendo importados, cada um com potencialmente muitos extrínsecos.

Para obter o número do extrínseco `i`, você pode encadear a função [`enumerate()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.enumerate) após o `into_iter()`.

## Exercícios:

### Construa Sua Função de Execução de Bloco

Agora você deve ter todas as ferramentas e informações necessárias para escrever com sucesso sua função `execute_block`.

Siga os `TODO`s fornecidos pelo template e certifique-se de incluir o `impl crate::support::Dispatch for Runtime` que fornecemos para você e que implementaremos nos próximos passos.

No `main.rs`:

```rust
mod balances;
mod support;
mod system;

/* TODO: Importe `crate::support::Dispatch` para que você possa acessar a função `dispatch`. */

/// ...código anterior.

// Estas são todas as chamadas expostas para o mundo.
// Note que é apenas uma acumulação das chamadas expostas por cada módulo.
pub enum RuntimeCall {
// TODO: Não implementado ainda.
}

// Este é o nosso Runtime principal.
// Ele acumula todos os diferentes pallets que queremos usar.
#[derive(Debug)]
pub struct Runtime {
system: system::Pallet<Self>,
balances: balances::Pallet<Self>,
}

impl system::Config for Runtime {
type AccountId = types::AccountId;
type BlockNumber = types::BlockNumber;
type Nonce = types::Nonce;
}

impl balances::Config for Runtime {
type Balance = types::Balance;
}

impl Runtime {
// Cria uma nova instância do Runtime principal, criando uma nova instância de cada pallet.
fn new() -> Self {
Self { system: system::Pallet::new(), balances: balances::Pallet::new() }
}

// Executa um bloco de extrínsecos. Incrementa o número do bloco.
fn execute_block(&mut self, block: types::Block) -> support::DispatchResult {
/* TODO:
- Incrementar o número do bloco do sistema.
- Verifique se o número do bloco de entrada corresponde ao número do bloco atual,
ou retornar um erro.
- Iterar sobre os extrínsecos do bloco...
- Aumente o nonce do chamador.
- Despache o extrínseco usando o `caller` e a `call` contida no extrínseco.
- Lidar com erros de `despacho` da mesma forma que fizemos para chamadas individuais: imprimindo qualquer
erro e capturar o resultado.
- Você pode estender a mensagem de erro para incluir informações como o número do bloco e
número extrínseco.
*/
Ok(())
}
}

//também ADICIONE ESTE CÓDIGO AO SEU arquivo main.rs:
impl crate::support::Dispatch for Runtime {
type Caller = <Runtime as system::Config>::AccountId;
type Call = RuntimeCall;

// Despacha uma chamada em nome de um chamador. Aumenta o nonce do chamador.
//
// Dispatch nos permite identificar qual chamada de módulo subjacente queremos executar.
// Observe que extraímos o `chamador` do extrínseco e usamos essa informação
// para determinar em nome de quem estamos executando a chamada.
fn dispatch(
&mut self,
caller: Self::Caller,
runtime_call: Self::Call,
) -> support::DispatchResult {
unimplemented!();
}
}

/// ...código anterior.
```

Executar blocos é um aspecto fundamental da funcionalidade blockchain, e agora você deu um passo significativo para dominar esse processo. Parabéns por alcançar esse marco!

Se você tiver alguma dúvida ou encontrar problemas, não hesite em pedir ajuda no canal [#🆘・section-4](https://discord.com/channels/898706705779687435/980905761783832637) no Discord. Estamos aqui para te apoiar! 🚀
Loading
Loading