Noções Básicas de Blockchain em Rust
9 exemplos para você começar com o desenvolvimento de blockchain em Rust - 7 básicos e 2 intermediários.
Pré-requisitos
cargo new chain-playground && cd chain-playground
cargo add sha2 ed25519-dalek hex serde --features derive
cargo add tokio --features fullFerramentas: Os exemplos visam a Rust 1.97.0 (edição 2024). Alvos on-chain podem usar edições mais antigas por SDK - siga o template de cada cadeia.
Exemplos Básicos
1. Hash de dados com SHA-256
IDs de blocos e transações começam com hashes criptográficos.
use sha2::{Digest, Sha256};
fn hash_hex(data: &[u8]) -> String {
let digest = Sha256::digest(data);
hex::encode(digest)
}- SHA-256 é onipresente em árvores de Merkle estilo Bitcoin e IDs de conteúdo.
- Sempre aplique hash à codificação canônica de bytes, não JSON com ordem de chaves instável.
- Exiba como hex para logs; armazene bytes brutos on-chain quando o espaço for importante.
Relacionado: Criptografia Aplicada - catálogo de primitivas
2. Gerar um par de chaves Ed25519
Cadeias modernas usam Ed25519 para assinaturas rápidas e chaves pequenas.
use ed25519_dalek::SigningKey;
use rand::rngs::OsRng;
fn new_keypair() -> SigningKey {
SigningKey::generate(&mut OsRng)
}SigningKeydeve permanecer secreto;VerifyingKeyé público.- Use RNG do sistema operacional, nunca seeds codificadas em produção.
- Persista chaves criptografadas em repouso - veja a página de carteiras.
Relacionado: Carteiras e Gerenciamento de Chaves
3. Assinar e verificar uma mensagem
Assinaturas provam autorização sem revelar a chave privada.
use ed25519_dalek::{Signer, SigningKey, Verifier, VerifyingKey};
fn sign_verify(key: &SigningKey, msg: &[u8]) -> bool {
let sig = key.sign(msg);
let vk: VerifyingKey = key.verifying_key();
vk.verify(msg, &sig).is_ok()
}- Verifique antes de agir sobre mensagens de peers em manipuladores P2P.
- Inclua strings de separação de domínio em
msgpara evitar replay entre protocolos. - Verificação em tempo constante é importante em validadores voltados para a internet pública.
4. Serializar com serde para configurações determinísticas
Configurações de cadeia e arquivos de genesis usam serialização estruturada.
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct GenesisAccount {
pubkey: String,
balance: u64,
}- Prefira inteiros de largura fixa (
u64) para valores de token em configurações. - Documente decimais off-chain; on-chain frequentemente usa apenas as menores unidades.
- Versionar o esquema de genesis para migrar testnets com segurança.
5. Modelar um saldo de conta com matemática verificada
Pânicos de overflow se tornam exploits em contratos de token.
fn transfer(balance: u64, amount: u64) -> Option<u64> {
balance.checked_sub(amount)
}checked_*retornaNoneem caso de overflow em vez de envolver.- Nunca use
f64para valores de token em código de consenso. - Corresponder às regras do programa de token (taxas, aluguel) no mesmo domínio de inteiros.
Relacionado: Segurança e Auditoria - bugs de inteiros
6. Representar uma struct de transação simples
Mantenha structs on-chain pequenas e fáceis de copiar onde os programas exigirem.
#[derive(Clone, Debug)]
struct Tx {
from: [u8; 32],
to: [u8; 32],
lamports: u64,
}- Arrays fixos para pubkeys evitam alocação de heap em caminhos críticos.
- Separe o formato de fio (Borsh/Anchor) dos tipos de domínio internos quando útil.
- Valide todos os campos no ponto de entrada do programa antes de mutar o estado.
Relacionado: Programas Solana
7. Esboço de cliente JSON-RPC assíncrono
Nós expõem APIs HTTP/WebSocket para interação com a cadeia.
use reqwest::Client;
use serde_json::json;
async fn get_balance(client: &Client, url: &str, address: &str) -> reqwest::Result<serde_json::Value> {
let body = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "getBalance",
"params": [address]
});
let res = client.post(url).json(&body).send().await?;
res.json().await
}- Defina timeout para chamadas RPC; endpoints públicos limitam taxas agressivamente.
- Tente novamente com backoff apenas para leituras idempotentes, não envios.
- Execute validadores e bots no Tokio com concorrência limitada.
Exemplos Intermediários
8. Raiz de Merkle sobre hashes de transação
Clientes leves verificam a inclusão com provas de Merkle.
use sha2::{Digest, Sha256};
fn merkle_root(leaves: &[Vec<u8>]) -> [u8; 32] {
if leaves.is_empty() {
return [0u8; 32];
}
let mut layer: Vec<[u8; 32]> = leaves.iter().map(|l| Sha256::digest(l).into()).collect();
while layer.len() > 1 {
let mut next = Vec::new();
for chunk in layer.chunks(2) {
let mut h = Sha256::new();
h.update(chunk[0]);
h.update(chunk.get(1).unwrap_or(&chunk[0]));
next.push(h.finalize().into());
}
layer = next;
}
layer[0]
}- Duplique a última folha quando a contagem for ímpar - os esquemas variam, documente sua regra.
- A verificação da prova é mais barata do que reexecutar todas as transações.
- Teste de propriedade de estabilidade da raiz contra implementações de referência.
Relacionado: Consenso e P2P
9. Dependência de SDK de cadeia com feature-gated
Mantenha ferramentas off-chain separadas de crates de programas on-chain.
[dependencies]
anchor-lang = { version = "0.30", optional = true }
[features]
on-chain = ["anchor-lang"]- Builds on-chain visam BPF/SBF com limites de tamanho e chamadas de sistema.
- Testes de integração off-chain usam Tokio e reqwest completos.
- Um repositório, dois alvos Cargo via workspaces - veja a página Substrate.
Relacionado: Substrate e ink!
Versões da Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4 e Polars 0.46+.