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
web3crate para Rust — https://docs.rs/web3ethers-rs— biblioteca Ethereum completa — https://docs.rs/etherssecp256k1crate — 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