From 5cf10b0d0524546f662aa3f35c6db3f84d18ea0a Mon Sep 17 00:00:00 2001 From: Vini Black Date: Mon, 22 Jul 2024 15:39:54 -0300 Subject: [PATCH] Improving section 1 text --- .../Section_1/Lesson_1_Balances_Pallet.md | 48 +++++----- .../pt-BR/Section_1/Lesson_2_Add_State.md | 57 ++++++------ .../Section_1/Lesson_3_Store_And_Read.md | 92 ++++++++++--------- .../pt-BR/Section_1/Lesson_4_Basic_Tests.md | 76 ++++++++------- .../Section_1/Lesson_5_Transfer_Balance.md | 63 +++++++------ 5 files changed, 174 insertions(+), 162 deletions(-) diff --git a/Rust_State_Machine/pt-BR/Section_1/Lesson_1_Balances_Pallet.md b/Rust_State_Machine/pt-BR/Section_1/Lesson_1_Balances_Pallet.md index 226c9af88..917b89eb8 100644 --- a/Rust_State_Machine/pt-BR/Section_1/Lesson_1_Balances_Pallet.md +++ b/Rust_State_Machine/pt-BR/Section_1/Lesson_1_Balances_Pallet.md @@ -18,7 +18,7 @@ Como mencionado anteriormente, no coração de uma blockchain está uma máquina Podemos criar uma máquina de estados muito ingênua usando abstrações simples de Rust, e através disso aprender sobre Rust no contexto de blockchains. -Queremos manter nosso código organizado, então não vamos realmente começar a construir no arquivo main.rs, mas sim em módulos Rust separados. Podemos pensar no arquivo main.rs como a cola que une tudo, e veremos isso ao longo deste projeto. +Queremos manter nosso código organizado, então não vamos realmente começar a construir no arquivo `main.rs`, mas sim em módulos Rust separados. Podemos pensar no arquivo `main.rs` como a cola que une tudo, e veremos isso ao longo deste projeto. "Pallet" é um termo específico do Polkadot SDK, que se refere a módulos Rust que contêm lógica específica para o runtime da sua blockchain. Vamos começar a usar esse termo aqui porque o que construímos aqui se assemelhará muito ao que você verá com o Polkadot SDK. @@ -34,51 +34,53 @@ Este é um ótimo ponto de partida, e o primeiro Pallet que vamos construir. 1. Crie um novo arquivo na pasta `src` chamado `balances.rs` -```bash -touch src/balances.rs -``` + ```bash + touch src/balances.rs + ``` 2. Neste arquivo, crie uma `struct`, que atuará como estado e ponto de entrada para este módulo: -```rust -pub struct Pallet {} -``` + ```rust + pub struct Pallet {} + ``` 3. Agora volte para `src/main.rs` e importe este novo módulo, que incluirá toda a lógica dentro dele: -```rust -mod balances; -``` + ```rust + mod balances; + ``` 4. Se executarmos seu programa agora, você verá que ele ainda é compilado e executado, mas poderá mostrar alguns avisos como: -```rust -warning: struct `Pallet` is never constructed ---> src/balances.rs:1:12 -| -1 | pub struct Pallet { } -| ^^^^^^ -| -= note: `#[warn(dead_code)]` on by default - -warning: `pr` (bin "pr") generated 1 warning -``` + ```rust + warning: struct `Pallet` is never constructed + --> src/balances.rs:1:12 + | + 1 | pub struct Pallet { } + | ^^^^^^ + | + = note: `#[warn(dead_code)]` on by default + + warning: `pr` (bin "pr") generated 1 warning + ``` Tudo bem! Ainda não começamos a usar nosso Pallet, mas você pode ver que o compilador Rust está detectando nosso novo código e trazendo essa lógica para nosso programa principal. Este é o início da construção do nosso primeiro módulo de máquina de estados. ## Exercícios: Em `balances.rs`: + ```rust /* TODO: crie uma nova struct pública chamada `Pallet`. */ ``` -On `main.rs`: +Em `main.rs`: + ```rust /* TODO: use seu novo módulo `balances` */ fn main() { - println!("Hello, world!"); + println!("Hello, world!"); } ``` diff --git a/Rust_State_Machine/pt-BR/Section_1/Lesson_2_Add_State.md b/Rust_State_Machine/pt-BR/Section_1/Lesson_2_Add_State.md index 32282ed35..7a7b632a0 100644 --- a/Rust_State_Machine/pt-BR/Section_1/Lesson_2_Add_State.md +++ b/Rust_State_Machine/pt-BR/Section_1/Lesson_2_Add_State.md @@ -14,35 +14,34 @@ Para isso, usaremos um `BTreeMap`, que podemos importar da biblioteca `std` do R 1. Importe o objeto `BTreeMap`. -```rust -use std::collections::BTreeMap; -``` + ```rust + use std::collections::BTreeMap; + ``` 2. Crie um campo `balances` em `Pallet` usando o `BTreeMap`. + Para a `chave`, usaremos uma string estática simples por enquanto. Dessa forma, podemos acessar usuários como `"alice"`, `"bob"`, etc. Isso será alterado no futuro. -Para a `chave`, usaremos uma string estática simples por enquanto. Dessa forma, podemos acessar usuários como `"alice"`, `"bob"`, etc... Isso será alterado no futuro. + Para o `valor`, usaremos um `u128`, que é o maior tipo suportado nativamente no Rust. Isso permitirá que nossos usuários tenham saldos muito grandes se quisermos. -Para o `valor`, usaremos um `u128`, que é o maior tipo suportado nativamente no Rust. Isso permitirá que nossos usuários tenham saldos muito grandes se quisermos. + No final, isso ficará assim: -No final, isso ficará assim: - -```rust -pub struct Pallet { - balances: BTreeMap, -} -``` + ```rust + pub struct Pallet { + balances: BTreeMap, + } + ``` -1. Finalmente, precisamos de uma maneira de inicializar este objeto e seu estado. Para isso, vamos implementar uma função na `Pallet` chamada `fn new()`: +3. Finalmente, precisamos de uma maneira de inicializar este objeto e seu estado. Para isso, vamos implementar uma função na `Pallet` chamada `fn new()`: -```rust -impl Pallet { - pub fn new() -> Self { - Self { - balances: BTreeMap::new() - } - } -} -``` + ```rust + impl Pallet { + pub fn new() -> Self { + Self { + balances: BTreeMap::new(), + } + } + } + ``` ## Exercício: @@ -52,16 +51,16 @@ No `balances.rs`: use std::collections::BTreeMap; pub struct Pallet { - // Um armazenamento simples mapeando contas (`String`) para seus saldos (`u128`). - /* TODO: Adicionar um campo `balances` que é um `BTreeMap` de `String` para `u128`. */ + // Um armazenamento simples mapeando contas (`String`) para seus saldos (`u128`). + /* TODO: Adicionar um campo `balances` que é um `BTreeMap` de `String` para `u128`. */ } impl Pallet { - /// Cria uma nova instância do módulo de saldos. - pub fn new() -> Self { - /* TODO: Retornar uma nova instância da struct `Pallet`. */ - unimplemented!() // Remova esta linha após implementar a função - } + /// Cria uma nova instância do módulo de saldos. + pub fn new() -> Self { + /* TODO: Retornar uma nova instância da struct `Pallet`. */ + unimplemented!() // Remova esta linha após implementar a função + } } ``` diff --git a/Rust_State_Machine/pt-BR/Section_1/Lesson_3_Store_And_Read.md b/Rust_State_Machine/pt-BR/Section_1/Lesson_3_Store_And_Read.md index bdd1326cf..4647c0ae4 100644 --- a/Rust_State_Machine/pt-BR/Section_1/Lesson_3_Store_And_Read.md +++ b/Rust_State_Machine/pt-BR/Section_1/Lesson_3_Store_And_Read.md @@ -6,7 +6,7 @@ Você pode encontrar a [solução para a etapa anterior aqui](https://gist.githu Agora que estabelecemos os conceitos básicos do nosso módulo de saldos, vamos adicionar maneiras de interagir com ele. -Para fazer isso, continuaremos a criar mais funções implementadas em `Pallet` que concedem acesso para ler, escrever e atualizar o `balances: BTreeMap` que criamos. +Para fazer isso, continuaremos a criar mais funções implementadas em `Pallet` que concedem acesso para ler, escrever e atualizar o `balances: BTreeMap` que criamos. Finalmente, veremos como é realmente começar a interagir com nosso pallet de saldos a partir do arquivo `main.rs`. @@ -18,7 +18,7 @@ Antes de continuarmos, vamos dedicar um momento para revisar alguns conceitos de Um dos principais princípios de Rust é remover comportamento indefinido do seu código. -Uma maneira de ocorrer comportamento indefinido é permitindo que estados como `null` existam. Rust previne isso fazendo com que o usuário trate explicitamente todos os casos, e é aqui que entra a criação do tipo `Option` Dedique um momento para revisar [a seção sobre `Option`](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html?highlight=option#the-option-enum-and-its-advantages-over-null-values) do livro de Rust, se necessário. +Uma maneira de ocorrer comportamento indefinido é permitindo que estados como `null` existam. Rust previne isso fazendo com que o usuário trate explicitamente todos os casos, e é aqui que entra a criação do tipo `Option`. Dedique um momento para revisar [a seção sobre `Option`](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html?highlight=option#the-option-enum-and-its-advantages-over-null-values) do livro de Rust, se necessário. A API do `BTreeMap` usa um `Option` ao ler valores do mapa, já que pode ser que você peça para ler o valor de alguma chave que você não definiu. Por exemplo: @@ -38,23 +38,22 @@ A maneira mais verbosa é usando uma declaração de `match`: ```rust let maybe_value = map.get(&"alice"); match maybe_value { - Some(value) => { - // fazer algo com o `value` - }, - None => { - // talvez retornar um erro já que não havia valor lá - } + Some(value) => { + // fazer algo com o `value` + }, + None => { + // talvez retornar um erro já que não havia valor lá + } } ``` - -> 🚨 **Alerta:** O que você **NÃO DEVE** fazer é usar `unwrap()` cegamente em opções. Isso resultará em um `panic` no seu código, o que é exatamente o tipo de coisa que Rust foi projetado para prevenir! Em vez disso, você deve sempre tratar explicitamente todos os seus diferentes casos lógicos, e se deixar que Rust faça seu trabalho, seu código será super seguro. +> 🚨 **Alerta:** O que você **NÃO DEVE** fazer é usar `unwrap()` cegamente em opções. Isso resultará em um `panic` no seu código, o que é exatamente o tipo de coisa que Rust foi projetado para prevenir! Em vez disso, você deve sempre tratar explicitamente todos os seus diferentes casos lógicos, e se deixar que Rust faça seu trabalho, seu código será super seguro. No contexto do que estamos projetando para o módulo de saldos, temos um mapa que possui um número arbitrário de chaves de usuário e seus valores de saldo. O que devemos fazer quando lemos o saldo de um usuário que não existe no nosso mapa? -Bem, o truque aqui é que no contexto das blockchains, um usuário ter None saldo e um usuário ter `0` saldo é a mesma coisa. Claro, há alguns detalhes mais finos a serem expressos entre um usuário que existe em nosso estado com valor 0 e um usuário que não existe de todo, mas para os propósitos de nossas APIs, podemos tratá-los da mesma forma. +Bem, o truque aqui é que no contexto das blockchains, um usuário ter `None` saldo e um usuário ter `0` saldo é a mesma coisa. Claro, há alguns detalhes mais finos a serem expressos entre um usuário que existe em nosso estado com valor 0 e um usuário que não existe de todo, mas para os propósitos de nossas APIs, podemos tratá-los da mesma forma. Como isso se parece? @@ -81,53 +80,56 @@ Para tornar nosso módulo útil, precisamos ter pelo menos algumas funções que 1. Crie uma nova função dentro de `impl Pallet` chamada `fn set_balance`: - ```rust - impl Pallet { - pub fn set_balance(&mut self, who: &String, amount: u128) { - self.balances.insert(who, amount); - } + ```rust + impl Pallet { + pub fn set_balance(&mut self, who: &String, amount: u128) { + self.balances.insert(who.clone(), amount); + } - // -- snip -- - } - ``` + // -- snip -- + } + ``` - Como você pode ver, esta função simplesmente recebe informações sobre qual usuário queremos definir o saldo e qual saldo queremos definir. Isso então empurra essa informação para nosso `BTreeMap`, e isso é tudo. + Como você pode ver, esta função simplesmente recebe informações sobre qual usuário queremos definir o saldo e qual saldo queremos definir. Isso então empurra essa informação para nosso `BTreeMap`, e isso é tudo. 2. Crie uma nova função dentro de `impl Pallet` chamada `fn balance`: - ```rust - pub fn balance(&self, who: &String) -> u128 { - *self.balances.get(&who).unwrap_or(&0) - } - ``` + ```rust + impl Pallet { + pub fn balance(&self, who: &String) -> u128 { + *self.balances.get(&who).unwrap_or(&0) + } + } + ``` - Como você pode ver, esta função nos permite ler o saldo dos usuários em nosso mapa. A função permite que você insira algum usuário e nós retornaremos o saldo dele. + Como você pode ver, esta função nos permite ler o saldo dos usuários em nosso mapa. A função permite que você insira algum usuário e nós retornaremos o saldo dele. - > 🚨 **Alerta:** Note que fazemos nosso pequeno truque aqui! Em vez de expor uma API que força o usuário a lidar com um `Option` somos capazes de fazer nossa API sempre retornar um `u128` convertendo qualquer usuário com valor `None` em `0`. + > 🚨 **Alerta:** Note que fazemos nosso pequeno truque aqui! Em vez de expor uma API que força o usuário a lidar com um `Option`, somos capazes de fazer nossa API sempre retornar um `u128` convertendo qualquer usuário com valor `None` em `0`. ## Exercício: No `balances.rs`: + ```rust impl Pallet { - /// Cria uma nova instância do módulo de saldos. - pub fn new() -> Self { - Self { balances: BTreeMap::new() } - } - - /// Define o saldo de uma conta `who` para algum `amount`. - pub fn set_balance(&mut self, who: &String, amount: u128) { - /* Insira `amount` no BTreeMap sob `who`. */ - self.balances.insert(who.clone(), amount); - } - - /// Obtém o saldo de uma conta `who`. - /// Se a conta não tiver saldo armazenado, retornamos zero. - pub fn balance(&self, who: &String) -> u128 { - /* Retorna o saldo de `who`, retornando zero se `None`. */ - *self.balances.get(who).unwrap_or(&0) - } + /// Cria uma nova instância do módulo de saldos. + pub fn new() -> Self { + Self { balances: BTreeMap::new() } + } + + /// Define o saldo de uma conta `who` para algum `amount`. + pub fn set_balance(&mut self, who: &String, amount: u128) { + /* Insira `amount` no BTreeMap sob `who`. */ + unimplemented!() + } + + /// Obtém o saldo de uma conta `who`. + /// Se a conta não tiver saldo armazenado, retornamos zero. + pub fn balance(&self, who: &String) -> u128 { + /* Retorna o saldo de `who`, retornando zero se `None`. */ + unimplemented!() + } } ``` -A seguir, escreveremos nosso primeiro teste e realmente interagiremos com nosso módulo de saldos. Animado para a próxima etapa? Nós estamos! +A seguir, escreveremos nosso primeiro teste e realmente interagiremos com nosso módulo de saldos. Animado para a próxima etapa? Nós estamos! \ No newline at end of file diff --git a/Rust_State_Machine/pt-BR/Section_1/Lesson_4_Basic_Tests.md b/Rust_State_Machine/pt-BR/Section_1/Lesson_4_Basic_Tests.md index 6f405eaa8..b19a7f512 100644 --- a/Rust_State_Machine/pt-BR/Section_1/Lesson_4_Basic_Tests.md +++ b/Rust_State_Machine/pt-BR/Section_1/Lesson_4_Basic_Tests.md @@ -4,62 +4,72 @@ Você pode encontrar a [solução para a etapa anterior aqui](https://gist.githu [Youtube](https://youtu.be/Y_G6xJomcJU?si=1fzfU5gG5QYaI27A) -Agora que temos os conceitos básicos do nosso `Pallet` configurados, vamos realmente interagir com ele. +Agora que temos os conceitos básicos do nosso `Pallet` configurados, vamos realmente interagir com ele. Para isso, voltaremos ao arquivo `main.rs` e criaremos nosso primeiro `#[test]` que utilizará o código que escrevemos até agora. 1. No seu arquivo `src/balances.rs`, adicione um novo `#[test]` chamado `fn init_balances()`: - ```rust - #[test] - fn init_balances() { } - ``` + ```rust + #[test] + fn init_balances() { } + ``` 2. Para começar nosso teste, precisamos inicializar uma nova instância do nosso `Pallet`: - ```rust - #[test] - fn init_balances() { - let mut balances = balances::Pallet::new(); - } - ``` + ```rust + #[test] + fn init_balances() { + let mut balances = balances::Pallet::new(); + } + ``` - Observe que tornamos essa variável `mut` já que planejamos modificar nosso estado usando nossa API recém-criada. + Observe que tornamos essa variável `mut` já que planejamos modificar nosso estado usando nossa API recém-criada. 3. Finalmente, vamos verificar se nossas APIs de leitura e escrita estão funcionando como esperado: - ```rust - #[test] - fn init_balances() { - let mut balances = balances::Pallet::new(); + ```rust + #[test] + fn init_balances() { + let mut balances = balances::Pallet::new(); - assert_eq!(balances.balance("alice".to_string()), 0); - balances.set_balance("alice".to_string(), 100); - assert_eq!(balances.balance("alice".to_string()), 100); - assert_eq!(balances.balance("bob".to_string()), 0); - } - ``` + assert_eq!(balances.balance("alice".to_string()), 0); + balances.set_balance("alice".to_string(), 100); + assert_eq!(balances.balance("alice".to_string()), 100); + assert_eq!(balances.balance("bob".to_string()), 0); + } + ``` 4. Podemos executar nossos testes usando `cargo test`, onde esperamos que você veja que ele passa. Espero que neste ponto você possa começar a ver os primeiros passos da sua simples máquina de estados blockchain. # Exercício: + No `balances.rs`: + ```rust #[cfg(test)] -#[cfg(test)] mod tests { - use super::*; - - #[test] - fn init_balances() { - /* TODO: Crie uma variável mutável `balances`, que é uma nova instância de `Pallet`. */ - /* TODO: Verifique se o saldo de `alice` começa em zero. */ - /* TODO: Defina o saldo de `alice` para 100. */ - /* TODO: Verifique se o saldo de `alice` agora é 100. */ - /* TODO: Verifique se o saldo de `bob` não mudou e é 0. */ - } + use super::*; + + #[test] + fn init_balances() { + /* TODO: Crie uma variável mutável `balances`, que é uma nova instância de `Pallet`. */ + let mut balances = Pallet::new(); + + /* TODO: Verifique se o saldo de `alice` começa em zero. */ + assert_eq!(balances.balance(&"alice".to_string()), 0); + + /* TODO: Defina o saldo de `alice` para 100. */ + balances.set_balance(&"alice".to_string(), 100); + + /* TODO: Verifique se o saldo de `alice` agora é 100. */ + assert_eq!(balances.balance(&"alice".to_string()), 100); + + /* TODO: Verifique se o saldo de `bob` não mudou e é 0. */ + assert_eq!(balances.balance(&"bob".to_string()), 0); + } } ``` diff --git a/Rust_State_Machine/pt-BR/Section_1/Lesson_5_Transfer_Balance.md b/Rust_State_Machine/pt-BR/Section_1/Lesson_5_Transfer_Balance.md index 2008b9f7b..35ded97fc 100644 --- a/Rust_State_Machine/pt-BR/Section_1/Lesson_5_Transfer_Balance.md +++ b/Rust_State_Machine/pt-BR/Section_1/Lesson_5_Transfer_Balance.md @@ -1,6 +1,5 @@ Você pode encontrar a [solução para a etapa anterior aqui](https://gist.github.com/nomadbitcoin/03914776998cb74eaa0a6adaa7edacec). - # Habilitando Transferências de Saldos [Youtube](https://youtu.be/7Ue4xmpcDnk?si=5N8ukbaDizs5QXeq) @@ -44,7 +43,7 @@ enum Result { } ``` -`T` and `E` são parâmetros genéricos que permitem customizar o tipo de resultado conforme suas necessidades. Para os propósitos deste tutorial, sempre retornaremos `Ok(())` quando tudo ocorrer bem, e um `Err(&static str)` para descrever quaisquer erros com uma string básica. +`T` e `E` são parâmetros genéricos que permitem customizar o tipo de resultado conforme suas necessidades. Para os propósitos deste tutorial, sempre retornaremos `Ok(())` quando tudo ocorrer bem, e um `Err(&static str)` para descrever quaisquer erros com uma string básica. Você pode então definir o tipo `Result` assim: @@ -58,7 +57,7 @@ Você pode usar o tipo `Option` para acionar um `Err`, o que é útil quando voc Nesse contexto, queremos uma função que retorne um erro sempre que alguma operação de matemática segura retornar `None`. -Para isso, podemos encadear `ok_or` juntamente com `?` diretamente após a operação de matemática segura, assim: +Para isso, podemos encadear `ok_or` juntamente com `?` diretamente após a operação de matemática segura, assim: ```rust let new_from_balance = from_balance @@ -66,7 +65,7 @@ let new_from_balance = from_balance .ok_or("Not enough funds.")?; ``` -Se `checked_sub` retornar `None`, retornaremos um `Err` com a mensagem `"Not enough funds."` que pode ser exibida ao usuário. Caso contrário, se `checked_sub` retornar `Some(value)`, atribuiremos `new_from_balance` diretamente a esse valor. +Se `checked_sub` retornar `None`, retornaremos um `Err` com a mensagem `"Not enough funds."` que pode ser exibida ao usuário. Caso contrário, se `checked_sub` retornar `Some(value)`, atribuiremos `new_from_balance` diretamente a esse valor. Nesse caso, estamos escrevendo código que trata completamente o tipo `Option` de maneira segura e ergonômica. @@ -76,6 +75,7 @@ Nesse caso, estamos escrevendo código que trata completamente o tipo `Option` d 2. Crie um teste mostrando que tudo está funcionando conforme o esperado, incluindo o tratamento de erros. No `balances.rs`: + ```rust impl Pallet { /// ... código anterior. @@ -83,41 +83,40 @@ impl Pallet { /// Transfere `amount` de uma conta para outra. /// Esta função verifica se `caller` tem pelo menos `amount` de saldo para transferir, /// e se não ocorrem overflow/underflow matemáticos. - pub fn transfer( - &mut self, - caller: String, - to: String, - amount: u128, - ) -> Result<(), &'static str> { - /* TODO: - - Obter o saldo da conta `caller`. - - Obter o saldo da conta `to`. - - - Usar matemática segura para calcular um `new_caller_balance`. - - Usar matemática segura para calcular um `new_to_balance`. - - - Inserir o novo saldo de `caller`. - - Inserir o novo saldo de `to`. - */ - - Ok(()) - } + pub fn transfer( + &mut self, + caller: String, + to: String, + amount: u128, + ) -> Result<(), &'static str> { + /* TODO: + - Obter o saldo da conta `caller`. + - Obter o saldo da conta `to`. + - Usar matemática segura para calcular um `new_caller_balance`. + - Usar matemática segura para calcular um `new_to_balance`. + - Inserir o novo saldo de `caller`. + - Inserir o novo saldo de `to`. + */ + + Ok(()) + } } ``` + Também no `balances.rs`: ```rust mod tests { - /// ... previous code. + /// ... código anterior. #[test] - fn transfer_balance() { - /* TODO: Crie um teste que verifique o seguinte: - - Essa `alice` não pode transferir fundos que ela não possui. - - Essa `alice` pode transferir fundos para `bob` com sucesso. - - Que o saldo de `alice` e `bob` esteja atualizado corretamente. - */ - } + fn transfer_balance() { + /* TODO: Crie um teste que verifique o seguinte: + - Que `alice` não pode transferir fundos que ela não possui. + - Que `alice` pode transferir fundos para `bob` com sucesso. + - Que o saldo de `alice` e `bob` esteja atualizado corretamente. + */ + } } ``` @@ -126,4 +125,4 @@ E lembre-se, estamos disponíveis para responder a qualquer pergunta no [Discord ### 🌱 Crescendo Forte VOCÊ ESTÁ CHEGANDO LÁ. -A Seção 1 está concluída! Você está construindo uma base sólida. Muito bem :). +A Seção 1 está concluída! Você está construindo uma base sólida. Muito bem :). \ No newline at end of file