Markdown Converter
Agent skill for markdown-converter
This document defines coding conventions for all Rust projects, based on btc-vault contributing guidelines.
Sign in to like and favorite skills
This document defines coding conventions for all Rust projects, based on btc-vault contributing guidelines.
All code must pass:
cargo +nightly fmt --all with zero changescargo clippy with zero warningsFollow standard Rust code style with clarifications in
rustfmt.toml (if present). This applies to library code, applications, examples, and tests.
Always use inline variable interpolation in format strings:
✅ Correct:
println!("Hello {name}"); info!("Processing block {block_number} at slot {slot}"); error!("Failed to load file {path}: {error}"); debug!("Value: {value:#?}"); format!("Result: {result}");
❌ Incorrect:
println!("Hello {}", name); info!("Processing block {} at slot {}", block_number, slot); error!("Failed to load file {}: {}", path, error);
Applies to all formatting macros:
println!(), print!()format!()info!(), warn!(), error!(), debug!(), trace!()panic!(), assert!(), assert_eq!()write!(), writeln!()All
statements must appear in a single consolidated group at the top of each Rust file.use
✅ Correct:
use std::path::Path; use std::fs::File; use anyhow::Result; use serde::{Serialize, Deserialize}; use tracing::info; use crate::provers::generate_proof; use super::utils; fn my_function() { // Function code here }
❌ Incorrect:
use std::path::Path; fn my_function() { use std::fs::File; // NEVER import within functions // ... } use anyhow::Result; // NEVER split imports
Do NOT:
Exception: Only use non-top-level imports for conditional compilation or other strong technical reasons.
Avoid using
unless absolutely necessary.unsafe
When
unsafe is required:
// SAFETY: comments// SAFETY: This pointer is guaranteed to be valid because the buffer is allocated // with sufficient capacity and the index is bounds-checked above. unsafe { *ptr.add(index) = value }
Prefer safe abstractions:
Box::new_uninit().assume_init() instead of std::mem::zeroed() for types that don't permit zero-initializationMaybeUninit for uninitialized memoryAlways use checked arithmetic methods for value-related operations.
This prevents critical errors such as producing negative output values that can lead to invalid transactions.
✅ Correct:
let result = value.checked_add(amount) .ok_or(Error::Overflow)?; let difference = total.checked_sub(fee) .ok_or(Error::InsufficientFunds)?; let product = price.checked_mul(quantity) .ok_or(Error::Overflow)?;
❌ Incorrect:
let result = value + amount; // Can panic or overflow let difference = total - fee; let product = price * quantity;
Always prefer obvious code over clever code.
Write idiomatic Rust that is easy to understand and maintain.
Minimize use of
..unwrap()
Use
.expect() only when:
The correctness of
.expect() must be evident from:
If correctness cannot be guaranteed, use
Result<T, E>.
✅ Good:
// Correctness is obvious from context let config = Config::default(); let value = config.required_field.expect("Config::default always sets required_field"); // Better: use Result when not guaranteed fn load_config(path: &Path) -> Result<Config, Error> { let contents = fs::read_to_string(path)?; parse_config(&contents) }
❌ Avoid:
let value = some_option.unwrap(); // Why is this safe? let result = risky_operation().expect("should work"); // Vague reasoning
If code is incomplete or has known issues:
TODO comment explaining what remains// TODO: Handle edge case when block height exceeds u32::MAX // See issue #123
Prefer longer, descriptive variable names.
Short names (1-3 characters) are discouraged except:
id for entity identifiersi, j for simple loop counters✅ Good:
let transaction_hash = compute_hash(&tx); let block_height = get_latest_height(); let proving_key = load_key(&path)?;
❌ Avoid:
let h = compute_hash(&tx); // What is h? let bh = get_latest_height(); // Unclear abbreviation let pk = load_key(&path)?; // Too short
Keep dependencies sorted alphabetically.
Adding dependencies out of order introduces unnecessary entropy.
[dependencies] anyhow = "1.0" serde = "1.0" tokio = { version = "1.0", features = ["full"] } tracing = "0.1"
Pay attention to existing code structure and conventions in the project. When in doubt, follow the existing code style and do your best.
All
items must have doc-comments.pub
This improves auto-generated documentation via
rustdoc and enhances the experience for library users.
/// Generates a compressed proof using SP1. /// /// # Arguments /// /// * `prover` - The SP1 prover instance /// * `pkey` - The proving key for the circuit /// * `stdin` - Input data for the proof /// /// # Returns /// /// Returns the proof with public values and generation time in seconds. /// /// # Errors /// /// Returns `ProverError::GenerateProof` if proof generation fails. pub fn generate_compressed_proof( prover: &EnvProver, pkey: &SP1ProvingKey, stdin: &SP1Stdin, ) -> Result<(SP1ProofWithPublicValues, u64), ProverError>
Public modules and crates should begin with doc-comments explaining:
//! Proof generation and verification utilities. //! //! This module provides functions for generating zero-knowledge proofs //! using the SP1 zkVM. For usage examples, see the `examples/` directory.
Make each commit meaningful and self-contained.
Think of commits as a story: small, logical steps that build to the full picture.
For non-trivial changes:
A clean commit history helps reviewers but takes practice — it's encouraged, not strictly required.
NEVER use
or git add -A
- only stage modified files explicitly.git add .
✅ Correct:
# Stage specific modified files git add src/file1.rs src/file2.rs # Or stage all tracked files with updates git add -u
❌ Incorrect:
git add -A # NEVER - adds untracked files git add . # NEVER - adds untracked files
Rationale: Only commit files that are part of your changes. Untracked files (new documentation drafts, config files, etc.) should not be accidentally committed.
Always run these commands before committing:
cargo +nightly fmt --all cargo clippy
Both must complete with zero warnings or changes.
Every PR must pass the entire test suite before merging.
The PR author is responsible for:
Stale PRs may be closed.
Submit refactorings as standalone PRs.
Build on them in follow-up PRs. This keeps each PR focused and easier to review.
Avoid mixing unrelated changes:
Follow the Babylon project's PR rules: https://github.com/babylonlabs-io/babylon/blob/main/CONTRIBUTING.md#pull-request-rules
Prefer exhaustive matching:
match proof_type { ProofType::Compressed => handle_compressed(), ProofType::Groth16 => handle_groth16(), ProofType::Mock => handle_mock(), }
Avoid catch-all
_ unless truly needed.
Prefer borrowing over cloning:
// Good fn process_data(data: &[u8]) { } // Avoid unless necessary fn process_data(data: Vec<u8>) { }
&str for string slicesString for owned stringsfn format_message(prefix: &str, suffix: &str) -> String { format!("{prefix}: {suffix}") }
Use
? operator for clean error propagation:
fn load_and_process(path: &Path) -> Result<Data> { let file = File::open(path)?; let data = parse_file(file)?; Ok(data) }
All contributors are expected to maintain a respectful and professional attitude toward each other.
gh) on This MachineIMPORTANT: Due to the git repository being on a mounted filesystem (
/mnt/Zhitai0/...), the gh CLI cannot auto-detect the repository. Always use the --repo flag explicitly.
# ✅ Correct - always use --repo flag gh pr view 582 --repo babylonlabs-io/btc-vault gh pr edit 582 --repo babylonlabs-io/btc-vault --body "..." gh pr create --repo babylonlabs-io/btc-vault --title "..." --body "..." # ❌ Wrong - will fail with "not a git repository" error gh pr view 582 gh pr edit 582 --body "..."
For btc-vault repository: Use
--repo babylonlabs-io/btc-vault
When writing or modifying Rust code:
cargo +nightly fmt --all before committingunsafe unless absolutely necessary.unwrap() - prefer .expect() with clear reasoning or Resultcargo clippy and fix all warnings before committingWhen using GitHub CLI:
--repo babylonlabs-io/btc-vault flag for gh commands on this machine