Rust

Rust e Blockchain — Smart Contracts, Criptografia Aplicada e Web3 Já leu

18 min de leitura

Rust e Blockchain — Smart Contracts, Criptografia Aplicada e Web3
Rust — Artigo #45 Rust e Blockchain — Smart Contracts, Criptografia Aplicada e Web3 Por Prof. Dr. Marcelo Fontes | Série: Dominando Rust em 1 Ano Blockchain não

Rust — Artigo #45

Rust e Blockchain — Smart Contracts, Criptografia Aplicada e Web3

Por Prof. Dr. Marcelo Fontes | Série: Dominando Rust em 1 Ano


Blockchain não é apenas criptomoeda. É um problema de ciência da computação fascinante: como construir um registro imutável e verificável em um sistema distribuído onde nenhum participante confia nos outros? A resposta combina criptografia, estruturas de dados, consenso distribuído e teoria dos jogos.

Rust domina o espaço blockchain por razões evidentes: segurança de memória sem GC é crítica quando bugs custam milhões de dólares, performance é essencial para processar transações em escala, e o sistema de tipos expressa invariantes financeiros em tempo de compilação. Solana, NEAR Protocol e Polkadot são escritos em Rust. O compilador de smart contracts Solidity da Ethereum usa Rust internamente.


Fundamentos criptográficos

[dependencies]
sha2         = "0.10"
sha3         = "0.10"
hex          = "0.4"
rand         = "0.8"
secp256k1    = { version = "0.28", features = ["rand"] }
ripemd       = "0.1"
base58        = "0.2"
merkle_hash  = "3"
serde        = { version = "1", features = ["derive"] }
serde_json   = "1"
chrono       = { version = "0.4", features = ["serde"] }
anyhow       = "1"
use sha2::{Sha256, Digest};
use hex;

// Hash SHA-256 — base de toda a blockchain
fn sha256(dados: &[u8]) -> Vec<u8> {
    let mut hasher = Sha256::new();
    hasher.update(dados);
    hasher.finalize().to_vec()
}

fn sha256_hex(dados: &[u8]) -> String {
    hex::encode(sha256(dados))
}

// Double SHA-256 — usado no Bitcoin
fn sha256d(dados: &[u8]) -> Vec<u8> {
    sha256(&sha256(dados))
}

// Demonstra propriedades do hash
fn propriedades_hash() {
    println!("══ Propriedades do SHA-256 ══
");

    let dados = b"Transacao: Alice envia 1 BTC para Bob";
    let hash1 = sha256_hex(dados);
    println!("Dados: {}", String::from_utf8_lossy(dados));
    println!("Hash : {hash1}
");

    // Avalanche effect — uma mudança mínima muda o hash completamente
    let dados2 = b"Transacao: Alice envia 2 BTC para Bob";
    let hash2 = sha256_hex(dados2);
    println!("Dados: {}", String::from_utf8_lossy(dados2));
    println!("Hash : {hash2}
");

    // Contando bits diferentes
    let bits_diferentes = hash1.chars().zip(hash2.chars())
        .filter(|(a, b)| a != b)
        .count();
    println!("Caracteres hex diferentes: {}/{}", bits_diferentes, hash1.len());

    // Determinístico
    assert_eq!(sha256_hex(dados), sha256_hex(dados));
    println!("
Determinístico: ✓");

    // Irreversível — não dá para encontrar input a partir do output
    println!("Irreversível por design: ✓");
}

Merkle Tree — integridade de dados

use sha2::{Sha256, Digest};

#[derive(Debug, Clone)]
struct MerkleTree {
    folhas: Vec<Vec<u8>>,
    nos: Vec<Vec<Vec<u8>>>, // nos[0] = folhas, nos[n] = raiz
}

impl MerkleTree {
    fn novo(dados: Vec<Vec<u8>>) -> Self {
        if dados.is_empty() {
            return MerkleTree { folhas: vec![], nos: vec![] };
        }

        // Hashes das folhas
        let folhas: Vec<Vec<u8>> = dados.iter()
            .map(|d| hash_no(d))
            .collect();

        let mut niveis = vec![folhas.clone()];
        let mut nivel_atual = folhas.clone();

        // Constrói a árvore de baixo para cima
        while nivel_atual.len() > 1 {
            let mut proximo = Vec::new();

            for par in nivel_atual.chunks(2) {
                let hash = match par {
                    [a, b] => {
                        let mut combinado = a.clone();
                        combinado.extend_from_slice(b);
                        hash_no(&combinado)
                    }
                    [a] => a.clone(), // nó ímpar: sobe sozinho
                    _   => unreachable!(),
                };
                proximo.push(hash);
            }

            niveis.push(proximo.clone());
            nivel_atual = proximo;
        }

        MerkleTree { folhas, nos: niveis }
    }

    fn raiz(&self) -> Option<Vec<u8>> {
        self.nos.last()?.first().cloned()
    }

    fn raiz_hex(&self) -> String {
        self.raiz()
            .map(|r| hex::encode(r))
            .unwrap_or_else(|| "vazia".to_string())
    }

    // Gera prova de inclusão para o índice dado
    fn prova_inclusao(&self, indice: usize) -> Vec<(Vec<u8>, bool)> {
        let mut prova = Vec::new();
        let mut idx = indice;

        for nivel in &self.nos[..self.nos.len().saturating_sub(1)] {
            let irmao_idx = if idx % 2 == 0 { idx + 1 } else { idx - 1 };
            let e_direita = idx % 2 == 0;

            if irmao_idx < nivel.len() {
                prova.push((nivel[irmao_idx].clone(), e_direita));
            }

            idx /= 2;
        }

        prova
    }

    // Verifica prova de inclusão
    fn verificar_prova(
        raiz: &[u8],
        folha: &[u8],
        prova: &[(Vec<u8>, bool)],
    ) -> bool {
        let mut hash_atual = hash_no(folha);

        for (irmao, e_direita) in prova {
            let mut combinado = Vec::new();
            if *e_direita {
                // hash atual à esquerda, irmão à direita
                combinado.extend_from_slice(&hash_atual);
                combinado.extend_from_slice(irmao);
            } else {
                // irmão à esquerda, hash atual à direita
                combinado.extend_from_slice(irmao);
                combinado.extend_from_slice(&hash_atual);
            }
            hash_atual = hash_no(&combinado);
        }

        hash_atual == raiz
    }
}

fn hash_no(dados: &[u8]) -> Vec<u8> {
    let mut h = Sha256::new();
    h.update(dados);
    h.finalize().to_vec()
}

fn demonstrar_merkle() {
    println!("══ Merkle Tree ══
");

    let transacoes = vec![
        b"tx: Alice -> Bob: 1.0".to_vec(),
        b"tx: Bob -> Carol: 0.5".to_vec(),
        b"tx: Carol -> Dave: 0.3".to_vec(),
        b"tx: Dave -> Eve: 0.1".to_vec(),
    ];

    let arvore = MerkleTree::novo(transacoes.clone());

    println!("Transações: {}", transacoes.len());
    println!("Raiz Merkle: {}
", arvore.raiz_hex());

    // Prova de inclusão para tx 1 (Bob -> Carol)
    let idx = 1;
    let prova = arvore.prova_inclusao(idx);
    let raiz = arvore.raiz().unwrap();

    let valido = MerkleTree::verificar_prova(
        &raiz,
        &transacoes[idx],
        &prova,
    );

    println!("Prova de inclusão para tx[{idx}]:");
    println!("  Nós na prova: {}", prova.len());
    println!("  Válida: {}", if valido { "✓" } else { "✗" });

    // Tenta verificar com dado adulterado
    let tx_adulterada = b"tx: Bob -> Carol: 99.9".to_vec();
    let invalido = MerkleTree::verificar_prova(
        &raiz,
        &tx_adulterada,
        &prova,
    );
    println!("  Dado adulterado detectado: {}",
        if !invalido { "✓" } else { "✗ FALHA" });
}

Blockchain completa — implementação

use sha2::{Sha256, Digest};
use serde::{Serialize, Deserialize};
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Transacao {
    remetente: String,
    destinatario: String,
    valor: f64,
    taxa: f64,
    timestamp: u64,
    assinatura: String,  // simplificado — seria criptográfico
}

impl Transacao {
    fn nova(remetente: &str, destinatario: &str, valor: f64) -> Self {
        Transacao {
            remetente: remetente.to_string(),
            destinatario: destinatario.to_string(),
            valor,
            taxa: valor * 0.001, // 0.1% de taxa
            timestamp: timestamp_agora(),
            assinatura: format!("sig:{}:{}", remetente, valor),
        }
    }

    fn hash(&self) -> String {
        let dados = serde_json::to_string(self).unwrap();
        sha256_hex_local(dados.as_bytes())
    }

    fn e_coinbase(&self) -> bool {
        self.remetente == "COINBASE"
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Bloco {
    indice: u64,
    timestamp: u64,
    transacoes: Vec<Transacao>,
    hash_anterior: String,
    hash: String,
    nonce: u64,
    dificuldade: u32,
    merkle_root: String,
    minerador: String,
}

impl Bloco {
    fn genesis() -> Self {
        let mut bloco = Bloco {
            indice: 0,
            timestamp: timestamp_agora(),
            transacoes: vec![],
            hash_anterior: "0".repeat(64),
            hash: String::new(),
            nonce: 0,
            dificuldade: 2,
            merkle_root: "0".repeat(64),
            minerador: "genesis".to_string(),
        };
        bloco.hash = bloco.calcular_hash();
        bloco
    }

    fn calcular_hash(&self) -> String {
        let conteudo = format!(
            "{}{}{}{}{}{}{}",
            self.indice,
            self.timestamp,
            self.hash_anterior,
            self.nonce,
            self.dificuldade,
            self.merkle_root,
            self.minerador,
        );
        sha256_hex_local(conteudo.as_bytes())
    }

    fn calcular_merkle_root(transacoes: &[Transacao]) -> String {
        if transacoes.is_empty() {
            return "0".repeat(64);
        }
        let hashes: Vec<Vec<u8>> = transacoes.iter()
            .map(|t| hex::decode(t.hash()).unwrap_or_default())
            .collect();
        let arvore = MerkleTree::novo(hashes);
        arvore.raiz_hex()
    }

    fn minerar(
        &mut self,
        transacoes: Vec<Transacao>,
        dificuldade: u32,
        minerador: &str,
    ) {
        self.transacoes = transacoes;
        self.dificuldade = dificuldade;
        self.minerador = minerador.to_string();
        self.merkle_root = Self::calcular_merkle_root(&self.transacoes);

        let prefixo = "0".repeat(dificuldade as usize);

        println!(
            "  Minerando bloco #{} (dificuldade {})...",
            self.indice, dificuldade
        );

        loop {
            self.hash = self.calcular_hash();
            if self.hash.starts_with(&prefixo) {
                break;
            }
            self.nonce += 1;
        }

        println!(
            "  ✓ Bloco #{} minerado! nonce={} hash={}...{}",
            self.indice,
            self.nonce,
            &self.hash[..8],
            &self.hash[56..],
        );
    }

    fn e_valido(&self) -> bool {
        let prefixo = "0".repeat(self.dificuldade as usize);
        self.hash == self.calcular_hash()
            && self.hash.starts_with(&prefixo)
    }
}

struct Blockchain {
    chain: Vec<Bloco>,
    pool_transacoes: Vec<Transacao>,
    dificuldade: u32,
    recompensa_mineracao: f64,
    saldos: std::collections::HashMap<String, f64>,
}

impl Blockchain {
    fn nova(dificuldade: u32) -> Self {
        let genesis = Bloco::genesis();
        let mut bc = Blockchain {
            chain: vec![genesis],
            pool_transacoes: Vec::new(),
            dificuldade,
            recompensa_mineracao: 50.0,
            saldos: std::collections::HashMap::new(),
        };
        bc.saldos.insert("COINBASE".to_string(), f64::INFINITY);
        bc
    }

    fn ultimo_bloco(&self) -> &Bloco {
        self.chain.last().unwrap()
    }

    fn adicionar_transacao(&mut self, tx: Transacao) -> bool {
        // Valida saldo do remetente
        if !tx.e_coinbase() {
            let saldo = self.saldo_de(&tx.remetente);
            if saldo < tx.valor + tx.taxa {
                println!("  ✗ Saldo insuficiente: {} tem {:.2}, tenta enviar {:.2}",
                    tx.remetente, saldo, tx.valor + tx.taxa);
                return false;
            }
        }
        self.pool_transacoes.push(tx);
        true
    }

    fn minerar_bloco_pendente(&mut self, endereco_minerador: &str) {
        // Recompensa para o minerador
        let recompensa = Transacao {
            remetente: "COINBASE".to_string(),
            destinatario: endereco_minerador.to_string(),
            valor: self.recompensa_mineracao,
            taxa: 0.0,
            timestamp: timestamp_agora(),
            assinatura: "coinbase".to_string(),
        };

        let mut txs = std::mem::take(&mut self.pool_transacoes);
        txs.insert(0, recompensa);

        let mut novo_bloco = Bloco {
            indice: self.chain.len() as u64,
            timestamp: timestamp_agora(),
            transacoes: vec![],
            hash_anterior: self.ultimo_bloco().hash.clone(),
            hash: String::new(),
            nonce: 0,
            dificuldade: self.dificuldade,
            merkle_root: String::new(),
            minerador: endereco_minerador.to_string(),
        };

        novo_bloco.minerar(txs, self.dificuldade, endereco_minerador);

        // Atualiza saldos
        for tx in &novo_bloco.transacoes {
            *self.saldos.entry(tx.destinatario.clone()).or_insert(0.0) += tx.valor;
            if !tx.e_coinbase() {
                *self.saldos.entry(tx.remetente.clone()).or_insert(0.0)
                    -= tx.valor + tx.taxa;
            }
        }

        self.chain.push(novo_bloco);
    }

    fn saldo_de(&self, endereco: &str) -> f64 {
        *self.saldos.get(endereco).unwrap_or(&0.0)
    }

    fn e_valida(&self) -> bool {
        for i in 1..self.chain.len() {
            let atual = &self.chain[i];
            let anterior = &self.chain[i - 1];

            if !atual.e_valido() {
                println!("  ✗ Bloco #{} tem hash inválido", atual.indice);
                return false;
            }

            if atual.hash_anterior != anterior.hash {
                println!(
                    "  ✗ Bloco #{} quebra a cadeia (hash_anterior != hash do bloco {})",
                    atual.indice, anterior.indice
                );
                return false;
            }
        }
        true
    }

    fn exibir_status(&self) {
        println!("
── Status da Blockchain ──");
        println!("  Blocos: {}", self.chain.len());
        println!("  Txs pendentes: {}", self.pool_transacoes.len());
        println!("  Válida: {}", if self.e_valida() { "✓" } else { "✗" });

        println!("
── Saldos ──");
        let mut saldos: Vec<_> = self.saldos.iter()
            .filter(|(k, _)| k.as_str() != "COINBASE")
            .collect();
        saldos.sort_by(|a, b| a.0.cmp(b.0));
        for (endereco, saldo) in saldos {
            println!("  {:<12}: {:.4} MRC", endereco, saldo);
        }
    }
}

fn sha256_hex_local(dados: &[u8]) -> String {
    let mut h = Sha256::new();
    h.update(dados);
    hex::encode(h.finalize())
}

fn timestamp_agora() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_secs()
}

fn demo_blockchain() {
    println!("══ Blockchain Completa ══
");

    let mut bc = Blockchain::nova(2); // dificuldade 2 = hash começa com "00"

    // Bloco genesis já foi criado

    // Distribuição inicial (via coinbase)
    bc.adicionar_transacao(Transacao {
        remetente: "COINBASE".to_string(),
        destinatario: "Alice".to_string(),
        valor: 100.0,
        taxa: 0.0,
        timestamp: timestamp_agora(),
        assinatura: "genesis_dist".to_string(),
    });
    bc.adicionar_transacao(Transacao {
        remetente: "COINBASE".to_string(),
        destinatario: "Bob".to_string(),
        valor: 50.0,
        taxa: 0.0,
        timestamp: timestamp_agora(),
        assinatura: "genesis_dist".to_string(),
    });

    println!("Minerando bloco #1 (distribuição inicial)...");
    bc.minerar_bloco_pendente("Minerador_M");

    // Transações normais
    bc.adicionar_transacao(Transacao::nova("Alice", "Bob", 10.0));
    bc.adicionar_transacao(Transacao::nova("Bob", "Carol", 5.0));
    bc.adicionar_transacao(Transacao::nova("Alice", "Carol", 20.0));

    println!("
Minerando bloco #2 (transações)...");
    bc.minerar_bloco_pendente("Minerador_M");

    // Mais transações
    bc.adicionar_transacao(Transacao::nova("Carol", "Alice", 3.0));
    // Tentativa inválida — Bob não tem saldo suficiente
    bc.adicionar_transacao(Transacao::nova("Bob", "Alice", 999.0));

    println!("
Minerando bloco #3...");
    bc.minerar_bloco_pendente("Minerador_N");

    bc.exibir_status();

    // Demonstra imutabilidade — adulterar bloco invalida a cadeia
    println!("
── Testando Imutabilidade ──");
    println!("  Cadeia válida antes: {}",
        if bc.e_valida() { "✓" } else { "✗" });

    // Adultera uma transação no bloco 1
    bc.chain[1].transacoes[0].valor = 999.0;

    println!("  Após adulteração do bloco #1:");
    println!("  Cadeia válida: {}",
        if bc.e_valida() { "✓" } else { "✗ (adulteração detectada)" });
}

Smart Contracts — máquina virtual simples

use std::collections::HashMap;

// Bytecode da VM de smart contracts
#[derive(Debug, Clone)]
enum Instrucao {
    Push(i64),       // empurra valor na pilha
    Pop,             // remove topo da pilha
    Add,             // soma dois valores
    Sub,             // subtrai
    Mul,             // multiplica
    Div,             // divide
    Eq,              // igualdade
    Lt,              // menor que
    Jmp(usize),      // salta para instrução N
    JmpIf(usize),    // salta se topo for != 0
    Load(String),    // carrega variável
    Store(String),   // salva variável
    Call(String),    // chama função do contrato
    Return,          // retorna da função
    Halt,            // encerra execução
    Emit(String),    // emite evento
    Revert(String),  // reverte com mensagem
    GetCaller,       // endereço do chamador
    GetValue,        // valor enviado com a chamada
    GetBalance(String), // saldo de um endereço
    Transfer(String, i64), // transfere tokens
}

struct VMEstado {
    pilha: Vec<i64>,
    variaveis: HashMap<String, i64>,
    contador_programa: usize,
    gas_usado: u64,
    eventos: Vec<(String, Vec<i64>)>,
    caller: String,
    value: i64,
}

#[derive(Debug)]
enum ErroVM {
    PilhaVazia,
    DivisaoPorZero,
    GasEsgotado,
    Revertido(String),
    InstrucaoInvalida(usize),
}

struct VM {
    codigo: Vec<Instrucao>,
    gas_limite: u64,
    saldos: HashMap<String, i64>,
}

impl VM {
    fn novo(codigo: Vec<Instrucao>, gas_limite: u64) -> Self {
        VM {
            codigo,
            gas_limite,
            saldos: HashMap::new(),
        }
    }

    fn executar(
        &mut self,
        caller: &str,
        value: i64,
    ) -> Result<i64, ErroVM> {
        let mut estado = VMEstado {
            pilha: Vec::new(),
            variaveis: HashMap::new(),
            contador_programa: 0,
            gas_usado: 0,
            eventos: Vec::new(),
            caller: caller.to_string(),
            value,
        };

        loop {
            if estado.gas_usado >= self.gas_limite {
                return Err(ErroVM::GasEsgotado);
            }

            let instrucao = self.codigo
                .get(estado.contador_programa)
                .ok_or(ErroVM::InstrucaoInvalida(estado.contador_programa))?
                .clone();

            estado.contador_programa += 1;
            estado.gas_usado += custo_gas(&instrucao);

            match instrucao {
                Instrucao::Push(n) => {
                    estado.pilha.push(n);
                }

                Instrucao::Pop => {
                    estado.pilha.pop().ok_or(ErroVM::PilhaVazia)?;
                }

                Instrucao::Add => {
                    let b = pop(&mut estado.pilha)?;
                    let a = pop(&mut estado.pilha)?;
                    estado.pilha.push(a + b);
                }

                Instrucao::Sub => {
                    let b = pop(&mut estado.pilha)?;
                    let a = pop(&mut estado.pilha)?;
                    estado.pilha.push(a - b);
                }

                Instrucao::Mul => {
                    let b = pop(&mut estado.pilha)?;
                    let a = pop(&mut estado.pilha)?;
                    estado.pilha.push(a * b);
                }

                Instrucao::Div => {
                    let b = pop(&mut estado.pilha)?;
                    let a = pop(&mut estado.pilha)?;
                    if b == 0 {
                        return Err(ErroVM::DivisaoPorZero);
                    }
                    estado.pilha.push(a / b);
                }

                Instrucao::Eq => {
                    let b = pop(&mut estado.pilha)?;
                    let a = pop(&mut estado.pilha)?;
                    estado.pilha.push(if a == b { 1 } else { 0 });
                }

                Instrucao::Lt => {
                    let b = pop(&mut estado.pilha)?;
                    let a = pop(&mut estado.pilha)?;
                    estado.pilha.push(if a < b { 1 } else { 0 });
                }

                Instrucao::Jmp(dest) => {
                    estado.contador_programa = dest;
                }

                Instrucao::JmpIf(dest) => {
                    let cond = pop(&mut estado.pilha)?;
                    if cond != 0 {
                        estado.contador_programa = dest;
                    }
                }

                Instrucao::Load(nome) => {
                    let val = *estado.variaveis.get(&nome).unwrap_or(&0);
                    estado.pilha.push(val);
                }

                Instrucao::Store(nome) => {
                    let val = pop(&mut estado.pilha)?;
                    estado.variaveis.insert(nome, val);
                }

                Instrucao::Emit(evento) => {
                    let n_params = pop(&mut estado.pilha)?;
                    let params: Vec<i64> = (0..n_params)
                        .map(|_| estado.pilha.pop().unwrap_or(0))
                        .collect();
                    println!(
                        "  [Evento] {} {:?}", evento, params
                    );
                    estado.eventos.push((evento, params));
                }

                Instrucao::Revert(msg) => {
                    return Err(ErroVM::Revertido(msg));
                }

                Instrucao::GetCaller => {
                    // Simplificado: usa hash do endereço como número
                    let hash: i64 = estado.caller.bytes()
                        .fold(0i64, |acc, b| acc.wrapping_add(b as i64));
                    estado.pilha.push(hash);
                }

                Instrucao::GetValue => {
                    estado.pilha.push(estado.value);
                }

                Instrucao::GetBalance(endereco) => {
                    let saldo = *self.saldos.get(&endereco).unwrap_or(&0);
                    estado.pilha.push(saldo);
                }

                Instrucao::Transfer(dest, valor) => {
                    let saldo_contrato = self.saldos
                        .entry("contrato".to_string())
                        .or_insert(0);
                    if *saldo_contrato < valor {
                        return Err(ErroVM::Revertido(
                            "Saldo insuficiente no contrato".to_string()
                        ));
                    }
                    *saldo_contrato -= valor;
                    *self.saldos.entry(dest).or_insert(0) += valor;
                }

                Instrucao::Call(_nome) => {
                    // Implementação completa requereria jump table
                    println!("  [Call] função chamada");
                }

                Instrucao::Return => {
                    let resultado = estado.pilha.pop().unwrap_or(0);
                    println!("  Gas usado: {}", estado.gas_usado);
                    return Ok(resultado);
                }

                Instrucao::Halt => {
                    let resultado = estado.pilha.pop().unwrap_or(0);
                    println!("  Gas usado: {}", estado.gas_usado);
                    return Ok(resultado);
                }
            }
        }
    }
}

fn pop(pilha: &mut Vec<i64>) -> Result<i64, ErroVM> {
    pilha.pop().ok_or(ErroVM::PilhaVazia)
}

fn custo_gas(instrucao: &Instrucao) -> u64 {
    match instrucao {
        Instrucao::Push(_)     => 3,
        Instrucao::Pop         => 2,
        Instrucao::Add         => 3,
        Instrucao::Sub         => 3,
        Instrucao::Mul         => 5,
        Instrucao::Div         => 5,
        Instrucao::Store(_)    => 20, // escrita em storage é cara
        Instrucao::Load(_)     => 10,
        Instrucao::Transfer(_, _) => 100,
        Instrucao::Emit(_)     => 50,
        _                      => 1,
    }
}

fn demo_smart_contract() {
    println!("══ Smart Contract — Token ERC20 Simplificado ══
");

    // Contrato: token com transferência
    // Equivalente em Solidity:
    // function transfer(address to, uint amount) public {
    //     require(balances[msg.sender] >= amount, "Saldo insuficiente");
    //     balances[msg.sender] -= amount;
    //     balances[to] += amount;
    //     emit Transfer(msg.sender, to, amount);
    // }

    // Versão simplificada em bytecode:
    // Verifica saldo do chamador >= 50
    // Se ok, transfere 50 tokens
    let codigo = vec![
        // Carrega saldo atual (simulado)
        Instrucao::Push(100),          // 0: saldo do caller = 100
        Instrucao::Store("saldo".into()), // 1: saldo = 100

        // Verifica se saldo >= 50
        Instrucao::Load("saldo".into()), // 2
        Instrucao::Push(50),             // 3: valor a transferir
        Instrucao::Lt,                   // 4: saldo < 50?
        Instrucao::JmpIf(8),             // 5: se sim, vai para revert

        // Transferência bem-sucedida
        Instrucao::Push(50),             // 6
        Instrucao::Emit("Transfer".into()), // 7: emite evento
        Instrucao::Push(1),              // 8: retorna 1 (sucesso)
        Instrucao::Return,               // 9

        // Revert
        Instrucao::Revert("Saldo insuficiente".into()), // 8 (destino do JmpIf)
    ];

    let mut vm = VM::novo(codigo, 10_000);

    println!("Executando transferência de 50 tokens...");
    match vm.executar("Alice", 0) {
        Ok(resultado) =>
            println!("  Sucesso! Resultado: {resultado}"),
        Err(ErroVM::Revertido(msg)) =>
            println!("  Revertido: {msg}"),
        Err(e) =>
            println!("  Erro: {e:?}"),
    }
}

Assinatura digital com secp256k1

use secp256k1::{Secp256k1, Message, SecretKey, PublicKey};
use secp256k1::rand::rngs::OsRng;
use sha2::{Sha256, Digest};

fn demo_carteira() {
    println!("══ Carteira Criptográfica ══
");

    let secp = Secp256k1::new();

    // Gera par de chaves
    let (chave_privada, chave_publica) = secp.generate_keypair(&mut OsRng);

    println!("Chave privada : {}",
        hex::encode(chave_privada.secret_bytes()));
    println!("Chave pública : {}
",
        hex::encode(chave_publica.serialize()));

    // Deriva endereço (simplificado — Bitcoin usa hash160 da pubkey)
    let endereco = derivar_endereco(&chave_publica);
    println!("Endereço      : {endereco}
");

    // Assina uma transação
    let dados_tx = b"Alice envia 1.0 BTC para Bob em 2024-03-15";
    let hash_tx = {
        let mut h = Sha256::new();
        h.update(dados_tx);
        h.finalize()
    };

    let mensagem = Message::from_digest(hash_tx.into());
    let assinatura = secp.sign_ecdsa(&mensagem, &chave_privada);

    println!("Mensagem   : {}", String::from_utf8_lossy(dados_tx));
    println!("Hash TX    : {}", hex::encode(hash_tx));
    println!("Assinatura : {}...{}
",
        &hex::encode(assinatura.serialize_compact())[..16],
        &hex::encode(assinatura.serialize_compact())[112..]);

    // Verifica assinatura
    let valida = secp.verify_ecdsa(&mensagem, &assinatura, &chave_publica);
    println!("Assinatura válida    : {}",
        if valida.is_ok() { "✓" } else { "✗" });

    // Tenta verificar com outra chave pública (deve falhar)
    let (_, outra_pubkey) = secp.generate_keypair(&mut OsRng);
    let invalida = secp.verify_ecdsa(&mensagem, &assinatura, &outra_pubkey);
    println!("Chave errada rejeitada: {}",
        if invalida.is_err() { "✓" } else { "✗ FALHA" });

    // Tenta verificar mensagem adulterada (deve falhar)
    let dados_adulterados = b"Alice envia 99.0 BTC para Bob em 2024-03-15";
    let hash_adulterado = {
        let mut h = Sha256::new();
        h.update(dados_adulterados);
        h.finalize()
    };
    let msg_adulterada = Message::from_digest(hash_adulterado.into());
    let adulterado = secp.verify_ecdsa(&msg_adulterada, &assinatura, &chave_publica);
    println!("Adulteração detectada : {}",
        if adulterado.is_err() { "✓" } else { "✗ FALHA" });
}

fn derivar_endereco(pubkey: &PublicKey) -> String {
    // Simplificado — produção usa RIPEMD-160(SHA256(pubkey)) + checksum
    let bytes = pubkey.serialize();
    let mut h = Sha256::new();
    h.update(bytes);
    let hash = h.finalize();
    format!("1{}", &hex::encode(hash)[..33])
}

Proof of Work — mineração real

use sha2::{Sha256, Digest};
use std::time::Instant;

fn minerar_pow(dados: &str, dificuldade: u32) -> (u64, String, std::time::Duration) {
    let prefixo = "0".repeat(dificuldade as usize);
    let inicio = Instant::now();
    let mut nonce = 0u64;

    loop {
        let entrada = format!("{dados}{nonce}");
        let mut h = Sha256::new();
        h.update(entrada.as_bytes());
        let hash = hex::encode(h.finalize());

        if hash.starts_with(&prefixo) {
            return (nonce, hash, inicio.elapsed());
        }
        nonce += 1;
    }
}

fn demo_pow() {
    println!("══ Proof of Work ══
");

    let dados = "Bloco #42 | Merkle: abc123...";

    for dificuldade in 1..=5u32 {
        let (nonce, hash, tempo) = minerar_pow(dados, dificuldade);
        println!(
            "  Dificuldade {:2}: nonce={:8} hash={}... ({:.1}ms)",
            dificuldade,
            nonce,
            &hash[..16],
            tempo.as_secs_f64() * 1000.0,
        );
    }
}

fn main() {
    propriedades_hash();
    println!("
{}
", "═".repeat(55));

    demonstrar_merkle();
    println!("
{}
", "═".repeat(55));

    demo_blockchain();
    println!("
{}
", "═".repeat(55));

    demo_smart_contract();
    println!("
{}
", "═".repeat(55));

    demo_carteira();
    println!("
{}
", "═".repeat(55));

    demo_pow();
}

Rust em blockchains de produção

Solana usa Rust para todos os smart contracts (chamados "programs"). O runtime valida ownership e borrowing em tempo de compilação:

// Estrutura de um programa Solana (simplificada)
// use solana_program::prelude::*;
//
// #[derive(Accounts)]
// pub struct Transfer<'info> {
//     #[account(mut)]
//     pub de: Signer<'info>,
//     #[account(mut)]
//     pub para: SystemAccount<'info>,
// }
//
// pub fn transferir(ctx: Context<Transfer>, valor: u64) -> Result<()> {
//     let cpi = system_instruction::transfer(
//         ctx.accounts.de.key,
//         ctx.accounts.para.key,
//         valor,
//     );
//     invoke(&cpi, &[...])?;
//     Ok(())
// }

NEAR Protocol usa Rust para smart contracts compilados para WASM:

// Estrutura de um contrato NEAR (simplificada)
// use near_sdk::prelude::*;
//
// #[near_bindgen]
// #[derive(Default, BorshDeserialize, BorshSerialize)]
// pub struct Contador {
//     valor: i32,
// }
//
// #[near_bindgen]
// impl Contador {
//     pub fn incrementar(&mut self) {
//         self.valor += 1;
//         log!("Contador: {}", self.valor);
//     }
//
//     pub fn obter(&self) -> i32 {
//         self.valor
//     }
// }

Fontes e leituras recomendadas

  • "Bitcoin: A Peer-to-Peer Electronic Cash System" — Satoshi Nakamoto — o paper original — https://bitcoin.org/bitcoin.pdf
  • "Mastering Bitcoin" — Andreas Antonopoulos — livro gratuito — https://github.com/bitcoinbook/bitcoinbook
  • Solana Developer Docs — https://docs.solana.com/developing/programming-model/overview
  • NEAR Protocol Rust SDK — https://docs.near.org/sdk/rust/introduction
  • web3 crate para Rust — https://docs.rs/web3
  • ethers-rs — biblioteca Ethereum completa — https://docs.rs/ethers
  • secp256k1 crate — criptografia de curvas elípticas — https://docs.rs/secp256k1
  • "Programming Bitcoin" — Jimmy Song — implementa Bitcoin do zero — https://programmingbitcoin.com

Artigo #45 de 52 | Série: Dominando Rust em 1 Ano Próximo → Artigo #46: Rust e Machine Learning — Inferência, ONNX e aceleração de modelos


Comentários

Mais em Rust

Testes Avançados — Propriedades, Mocks e Benchmarks
Testes Avançados — Propriedades, Mocks e Benchmarks

No Artigo #16 cobrimos os fundamentos de testes em Rust — testes unitários, d...

Epílogo — O que Vem Depois, Como Continuar Crescendo, e a Filosofia do Rust
Epílogo — O que Vem Depois, Como Continuar Crescendo, e a Filosofia do Rust

Rust — Artigo #52 Epílogo — O que Vem Depois, Como Continuar Crescendo, e a F...

Generics — Código que Funciona para Qualquer Tipo
Generics — Código que Funciona para Qualquer Tipo

&nbsp; No artigo anterior aprendemos que traits definem&nbsp;o que um tipo p...