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 new file mode 100644 index 000000000..917b89eb8 --- /dev/null +++ b/Rust_State_Machine/pt-BR/Section_1/Lesson_1_Balances_Pallet.md @@ -0,0 +1,89 @@ +Já vi muitos tutoriais complicados, então vamos tentar manter este simples e organizado. 😃 Espero que você goste! + +# O Pallet de Saldos + +[Youtube](https://youtu.be/49NApUfg-w8?si=L1n02_Fg-NT45cVn) + +Nesta seção, vamos construir a primeira lógica para nossa máquina de estados: um Pallet de Saldos. + +Este Pallet gerenciará os saldos dos usuários e permitirá que eles transfiram tokens entre si. + +Ao longo do caminho, você aprenderá sobre matemática segura, opções, tratamento de erros e muito mais. + +Ao final desta seção, você terá projetado a lógica de uma criptomoeda simples. + +# Criando um Pallet de Saldos + +Como mencionado anteriormente, no coração de uma blockchain está uma máquina de estados. + +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. + +"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. + +## Saldos + +Praticamente toda blockchain tem lógica que lida com os saldos dos usuários nessa blockchain. + +Este Pallet dirá: quanto saldo cada usuário tem, fornecerá funções que permitem aos usuários transferir esses saldos e até mesmo algumas funções de baixo nível para permitir que seu sistema de blockchain manipule esses saldos, se necessário. Pense, por exemplo, se você quiser cunhar novos tokens que não existem ainda. + +Este é um ótimo ponto de partida, e o primeiro Pallet que vamos construir. + +## Criando uma Struct + +1. Crie um novo arquivo na pasta `src` chamado `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 {} + ``` + +3. Agora volte para `src/main.rs` e importe este novo módulo, que incluirá toda a lógica dentro dele: + + ```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 + ``` + +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`. */ +``` + +Em `main.rs`: + +```rust +/* TODO: use seu novo módulo `balances` */ + +fn main() { + println!("Hello, world!"); +} +``` + +Se você tiver uma pergunta sobre a qual está curioso, sinta-se à vontade para perguntar em [🆘・seção-1](https://discord.com/channels/898706705779687435/980904325763186788) no Discord. + +Normalmente, começo a entender realmente as coisas quando começo a programar. Então, vamos começar a construir algum código. :) \ No newline at end of file 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 new file mode 100644 index 000000000..7a7b632a0 --- /dev/null +++ b/Rust_State_Machine/pt-BR/Section_1/Lesson_2_Add_State.md @@ -0,0 +1,79 @@ +# Adicionando Estado ao Nosso Pallet + +[Youtube](https://youtu.be/CCxkdf2VX8w?si=nY_7Mze68Wkw12QR) + +Então, vamos adicionar algum estado simples ao nosso módulo `balances.rs`. + +Podemos fazer isso adicionando campos na nossa struct `Pallet`. + +Para um sistema de saldo, realmente só precisamos acompanhar uma coisa: **quanto saldo cada usuário tem em nosso sistema**. + +Para isso, usaremos um `BTreeMap`, que podemos importar da biblioteca `std` do Rust. + +`Maps` são objetos simples de `chave -> valor`, permitindo-nos definir um armazenamento de tamanho arbitrário onde podemos mapear algum identificador de usuário (`chave`) para o saldo da conta deles (`valor`). + +1. Importe o objeto `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 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: + + ```rust + pub struct Pallet { + balances: BTreeMap, + } + ``` + +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(), + } + } + } + ``` + +## Exercício: + +No `balances.rs`: + +```rust +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`. */ +} + +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 + } +} +``` + +Você pode confirmar neste ponto que tudo ainda deve estar compilando, e que você não cometeu nenhum erro pequeno. + +Em seguida, vamos realmente começar a **usar** este módulo. + +## Notas + +É importante notar que isso **NÃO** é como o armazenamento de Pallet funciona com o Polkadot SDK, mas apenas uma emulação simples dos comportamentos. + +No Polkadot SDK, existe uma camada de armazenamento separada que gerencia um banco de dados de chave-valor adequado que contém todas as informações (passadas e presentes) do nosso sistema de blockchain. Existem abstrações que se parecem e se comportam como um `BTreeMap` no Polkadot SDK, mas a lógica subjacente que mantém esses dados é muito mais complexa. + +Usar campos simples em uma struct mantém este projeto simples e ilustra que cada Pallet realmente deve gerenciar seu próprio armazenamento. No entanto, essa simplificação também leva a problemas se você projetar sistemas mais complexos onde múltiplos pallets interagem entre si. + +Não teremos nenhuma interação entre pallets neste projeto, no entanto, isso é definitivamente viável com o Polkadot SDK e um banco de dados adequado. \ No newline at end of file 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 new file mode 100644 index 000000000..4647c0ae4 --- /dev/null +++ b/Rust_State_Machine/pt-BR/Section_1/Lesson_3_Store_And_Read.md @@ -0,0 +1,135 @@ +Você pode encontrar a [solução para a etapa anterior aqui](https://gist.github.com/nomadbitcoin/d116a728d944026dd9fd4f3f689b75cf). + +# Adicionando Interação com Saldos + +[Youtube](https://youtu.be/coBI_avKIMw?si=qb-E3eBsOI_aFiOa) + +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. + +Finalmente, veremos como é realmente começar a interagir com nosso pallet de saldos a partir do arquivo `main.rs`. + +## Conhecimento Pré-requisito de Rust + +Antes de continuarmos, vamos dedicar um momento para revisar alguns conceitos de Rust que usaremos nesta próxima seção. + +### Option e Tratamento de Option + +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. + +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: + +```rust +use std::collections::BTreeMap; + +let mut map = BTreeMap::new(); +map.insert("alice", 100); +assert_eq!(map.get(&"alice"), Some(&100)); +assert_eq!(map.get(&"bob"), None); +``` + +Uma vez que temos um tipo `Option`, existem muitas maneiras diferentes de interagir com ele usando Rust. + +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á + } +} +``` + +> 🚨 **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. + +Como isso se parece? + +Bem, podemos usar `unwrap_or(...)` para tratar essa condição com segurança e tornar nossas futuras APIs mais ergonômicas de usar. Por exemplo: + +```rust +use std::collections::BTreeMap; + +let mut map = BTreeMap::new(); +map.insert("alice", 100); +assert_eq!(*map.get(&"alice").unwrap_or(&0), 100); +assert_eq!(*map.get(&"bob").unwrap_or(&0), 0); +``` + +Como você pode ver, ao usar `unwrap_or(&0)` após ler do nosso mapa, somos capazes de transformar nosso `Option` em um inteiro básico, onde usuários com algum valor têm seu valor exposto e usuários com `None` são transformados em `0`. + +Vamos ver como isso pode ser usado a seguir. + +## Definindo e Lendo Saldos de Usuários + +Como você pode ver, nossa máquina de estados inicial começa com todos sem saldo. + +Para tornar nosso módulo útil, precisamos ter pelo menos algumas funções que nos permitam criar novos saldos para usuários e ler esses saldos. + +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.clone(), amount); + } + + // -- 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. + +2. Crie uma nova função dentro de `impl Pallet` chamada `fn balance`: + + ```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. + + > 🚨 **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`. */ + 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! \ 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 new file mode 100644 index 000000000..b19a7f512 --- /dev/null +++ b/Rust_State_Machine/pt-BR/Section_1/Lesson_4_Basic_Tests.md @@ -0,0 +1,76 @@ +Você pode encontrar a [solução para a etapa anterior aqui](https://gist.github.com/nomadbitcoin/9171430e3a44c42513b5c1104ef972e2). + +# Teste Básico de Saldo + +[Youtube](https://youtu.be/Y_G6xJomcJU?si=1fzfU5gG5QYaI27A) + +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() { } + ``` + +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(); + } + ``` + + 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(); + + 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)] +mod tests { + 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); + } +} +``` + +Compartilhe seu [#progress](https://discord.com/channels/898706705779687435/980906289968345128) no Discord e faça o dia do Yan melhor! \ No newline at end of file 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 new file mode 100644 index 000000000..35ded97fc --- /dev/null +++ b/Rust_State_Machine/pt-BR/Section_1/Lesson_5_Transfer_Balance.md @@ -0,0 +1,128 @@ +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) + +Agora que inicializamos e começamos a usar nosso módulo de saldos, vamos adicionar provavelmente a API mais importante: `transfer`. + +## Aprender + +Antes de escrever nossa função, é importante revisar alguns dos princípios de blockchain e Rust. + +### Atores Maliciosos + +Em um sistema de blockchain, a segurança é primordial. Atores maliciosos podem tentar explorar vulnerabilidades, como saldos insuficientes durante transferências de fundos, ou problemas de overflow/underflow. As operações de matemática segura e mecanismos de tratamento de erros do Rust ajudam a mitigar esses riscos. + +### Matemática Segura + +As operações de matemática segura do Rust previnem overflow e underflow. Os métodos `checked_add` e `checked_sub` retornam uma `Option` que permite lidar com possíveis erros aritméticos de forma segura. + +No Rust, o tipo Option é uma parte fundamental da biblioteca padrão, projetada para lidar com cenários onde um valor pode ou não estar presente. É comumente usado em situações onde o resultado de uma operação pode ser indefinido ou ausente. + +Métodos como `checked_add` e `checked_sub` retornam `Option` para indicar sucesso ou falha devido a overflow ou underflow. + +```rust +let result = a.checked_add(b); +match result { + Some(sum) => println!("Sum: {}", sum), + None => println!("Overflow occurred."), +} +``` + +### Tratamento de Erros + +No Rust, o tratamento de erros é uma parte integral da escrita de código robusto e seguro. O tipo Result é comumente usado para funções que podem encontrar erros durante sua execução. + +O tipo Result é uma enum definida na biblioteca padrão. Ela tem duas variantes: `Ok(value)` para um resultado bem-sucedido e `Err(error)` para um erro: + +```rust +enum Result { + Ok(T), + Err(E), +} +``` + +`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: + +```rust +Result<(), &'static str> +``` + +### Options e Results + +Você pode usar o tipo `Option` para acionar um `Err`, o que é útil quando você só quer que sua função execute quando tudo corre conforme o esperado. + +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: + +```rust +let new_from_balance = from_balance + .checked_sub(amount) + .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. + +Nesse caso, estamos escrevendo código que trata completamente o tipo `Option` de maneira segura e ergonômica. + +# Exercícios: + +1. Crie uma função de transferência segura e simples no seu Pallet de Saldos. +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. + + /// 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(()) + } +} +``` + +Também no `balances.rs`: + +```rust +mod tests { + /// ... código anterior. + + #[test] + 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. + */ + } +} +``` + +E lembre-se, estamos disponíveis para responder a qualquer pergunta no [Discord](https://discord.com/channels/898706705779687435/980904325763186788). + +### 🌱 Crescendo Forte +VOCÊ ESTÁ CHEGANDO LÁ. + +A Seção 1 está concluída! Você está construindo uma base sólida. Muito bem :). \ No newline at end of file