Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: documentation and linting overhaul #549

Merged
merged 10 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,72 @@ repository = "https://github.com/Jon-Becker/heimdall-rs"
keywords = ["ethereum", "web3", "decompiler", "evm", "crypto"]
exclude = [".github/"]

[workspace.lints]
rust.missing_debug_implementations = "warn"
rust.missing_docs = "warn"
rust.unreachable_pub = "warn"
rust.unused_must_use = "deny"
rust.rust_2018_idioms = { level = "deny", priority = -1 }
rustdoc.all = "warn"

[workspace.lints.clippy]
# These are some of clippy's nursery (i.e., experimental) lints that we like.
# By default, nursery lints are allowed. Some of the lints below have made good
# suggestions which we fixed. The others didn't have any findings, so we can
# assume they don't have that many false positives. Let's enable them to
# prevent future problems.
branches_sharing_code = "warn"
clear_with_drain = "warn"
derive_partial_eq_without_eq = "warn"
empty_line_after_outer_attr = "warn"
equatable_if_let = "warn"
imprecise_flops = "warn"
iter_on_empty_collections = "warn"
iter_with_drain = "warn"
large_stack_frames = "warn"
manual_clamp = "warn"
mutex_integer = "warn"
needless_pass_by_ref_mut = "warn"
nonstandard_macro_braces = "warn"
or_fun_call = "warn"
path_buf_push_overwrite = "warn"
read_zero_byte_vec = "warn"
redundant_clone = "warn"
suboptimal_flops = "warn"
suspicious_operation_groupings = "warn"
trailing_empty_array = "warn"
trait_duplication_in_bounds = "warn"
transmute_undefined_repr = "warn"
trivial_regex = "warn"
tuple_array_conversions = "warn"
uninhabited_references = "warn"
unused_peekable = "warn"
unused_rounding = "warn"
useless_let_if_seq = "warn"

# These are nursery lints which have findings. Allow them for now. Some are not
# quite mature enough for use in our codebase and some we don't really want.
# Explicitly listing should make it easier to fix in the future.
as_ptr_cast_mut = "allow"
cognitive_complexity = "allow"
collection_is_never_read = "allow"
debug_assert_with_mut_call = "allow"
empty_line_after_doc_comments = "allow"
fallible_impl_from = "allow"
future_not_send = "allow"
iter_on_single_items = "allow"
missing_const_for_fn = "allow"
needless_collect = "allow"
non_send_fields_in_send_ty = "allow"
option_if_let_else = "allow"
redundant_pub_crate = "allow"
significant_drop_in_scrutinee = "allow"
significant_drop_tightening = "allow"
string_lit_as_bytes = "allow"
type_repetition_in_bounds = "allow"
unnecessary_struct_initialization = "allow"
use_self = "allow"

[workspace.dependencies]
heimdall-core = { path = "crates/core" }
heimdall-cache = { path = "crates/cache" }
Expand Down
3 changes: 3 additions & 0 deletions crates/cache/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ repository.workspace = true
keywords.workspace = true
exclude.workspace = true

[lints]
workspace = true

[lib]
bench = false

Expand Down
5 changes: 5 additions & 0 deletions crates/cache/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
//! Cache errors

/// Generic error type for heimdall cache operations
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Generic error
#[error("Error: {0}")]
Generic(String),
/// An IO error occurred
#[error("IO error: {0}")]
IOError(#[from] std::io::Error),
}
10 changes: 10 additions & 0 deletions crates/cache/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//! A simple cache system for heimdall-rs
//! Stores objects in ~/.bifrost/cache as bincode serialized files
//! Objects are stored with an expiry time, and are deleted if they are expired

use clap::Parser;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[allow(deprecated)]
Expand All @@ -17,6 +21,7 @@ pub(crate) mod util;
override_usage = "heimdall cache <SUBCOMMAND>"
)]
pub struct CacheArgs {
/// Cache subcommand
#[clap(subcommand)]
pub sub: Subcommands,
}
Expand All @@ -33,12 +38,15 @@ pub struct NoArguments {}
)]
#[allow(clippy::large_enum_variant)]
pub enum Subcommands {
/// Clear the cache, removing all objects
#[clap(name = "clean", about = "Removes all cached objects in ~/.bifrost/cache")]
Clean(NoArguments),

/// List all cached objects
#[clap(name = "ls", about = "Lists all cached objects in ~/.bifrost/cache")]
Ls(NoArguments),

/// Print the size of the cache in ~/.bifrost/cache
#[clap(name = "size", about = "Prints the size of the cache in ~/.bifrost/cache")]
Size(NoArguments),
}
Expand All @@ -47,7 +55,9 @@ pub enum Subcommands {
/// The expiry time is a unix timestamp
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Cache<T> {
/// The value stored in the cache
pub value: T,
/// The expiry time of the cache object
pub expiry: u64,
}

Expand Down
17 changes: 10 additions & 7 deletions crates/cache/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ use std::{
use crate::error::Error;

/// Decode a hex string into a bytearray
pub fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
pub(crate) fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
(0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16)).collect()
}

/// Encode a bytearray into a hex string
pub fn encode_hex(s: Vec<u8>) -> String {
pub(crate) fn encode_hex(s: Vec<u8>) -> String {
s.iter().fold(String::new(), |mut acc: String, b| {
write!(acc, "{b:02x}", b = b).expect("unable to write");
acc
})
}

/// Prettify bytes into a human-readable format \
pub fn prettify_bytes(bytes: u64) -> String {
/// Prettify bytes into a human-readable format
pub(crate) fn prettify_bytes(bytes: u64) -> String {
if bytes < 1024 {
format!("{bytes} B")
} else if bytes < 1024 * 1024 {
Expand All @@ -39,7 +39,8 @@ pub fn prettify_bytes(bytes: u64) -> String {
}

/// Write contents to a file on the disc
pub fn write_file(path_str: &str, contents: &str) -> Result<(), Error> {
/// If the parent directory does not exist, it will be created
pub(crate) fn write_file(path_str: &str, contents: &str) -> Result<(), Error> {
let path = Path::new(path_str);

if let Some(prefix) = path.parent() {
Expand All @@ -58,7 +59,8 @@ pub fn write_file(path_str: &str, contents: &str) -> Result<(), Error> {
}

/// Read contents from a file on the disc
pub fn read_file(path: &str) -> Result<String, Error> {
/// Returns the contents as a string
pub(crate) fn read_file(path: &str) -> Result<String, Error> {
let path = Path::new(path);
let mut file = File::open(path)
.map_err(|e| Error::IOError(std::io::Error::new(std::io::ErrorKind::Other, e)))?;
Expand All @@ -68,7 +70,8 @@ pub fn read_file(path: &str) -> Result<String, Error> {
}

/// Delete a file or directory on the disc
pub fn delete_path(_path: &str) -> bool {
/// Returns true if the operation was successful
pub(crate) fn delete_path(_path: &str) -> bool {
let path = match std::path::Path::new(_path).to_str() {
Some(path) => path,
None => return false,
Expand Down
9 changes: 8 additions & 1 deletion crates/cfg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ repository.workspace = true
keywords.workspace = true
exclude.workspace = true

[lints]
workspace = true

[lib]
bench = false

Expand All @@ -25,7 +28,11 @@ eyre = "0.6.12"
futures = "0.3.30"
lazy_static = "1.4.0"
petgraph = "0.6.2"
alloy = { version = "0.3.3", features = ["full", "rpc-types-debug", "rpc-types-trace"] }
alloy = { version = "0.3.3", features = [
"full",
"rpc-types-debug",
"rpc-types-trace",
] }

heimdall-disassembler.workspace = true
heimdall-vm.workspace = true
3 changes: 1 addition & 2 deletions crates/cfg/src/core/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ use petgraph::{matrix_graph::NodeIndex, Graph};

/// convert a symbolic execution [`VMTrace`] into a [`Graph`] of blocks, illustrating the
/// control-flow graph found by the symbolic execution engine.
// TODO: should this be a trait for VMTrace to implement?
pub fn build_cfg(
pub(crate) fn build_cfg(
vm_trace: &VMTrace,
contract_cfg: &mut Graph<String, String>,
parent_node: Option<NodeIndex<u32>>,
Expand Down
4 changes: 4 additions & 0 deletions crates/cfg/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ use super::CfgArgs;
use crate::{core::graph::build_cfg, error::Error};
use tracing::{debug, info};

/// The result of the cfg command. Contains the generated control flow graph.
#[derive(Debug, Clone)]
pub struct CfgResult {
/// The generated control flow graph of the contract.
pub graph: Graph<String, String>,
}

impl CfgResult {
/// Returns the control flow graph as a graphviz formatted string.
pub fn as_dot(&self, color_edges: bool) -> String {
let output = format!("{}", Dot::with_config(&self.graph, &[]));

Expand All @@ -44,6 +47,7 @@ impl CfgResult {
}
}

/// Generates a control flow graph for the target contract.
pub async fn cfg(args: CfgArgs) -> Result<CfgResult, Error> {
// init
let start_time = Instant::now();
Expand Down
6 changes: 6 additions & 0 deletions crates/cfg/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
//! CFG Errors

/// Generic error type for the CFG Module
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Error when trying to fetch information from the chain
#[error("Fetch error: {0}")]
FetchError(String),
/// Error when disassembling contract bytecode
#[error("Disassembly error: {0}")]
DisassemblyError(#[from] heimdall_disassembler::Error),
/// Generic error
#[error("Internal error: {0}")]
Eyre(#[from] eyre::Report),
}
3 changes: 3 additions & 0 deletions crates/cfg/src/interfaces/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use eyre::Result;
use heimdall_common::ether::bytecode::get_bytecode_from_target;
use heimdall_config::parse_url_arg;

/// Arguments for the CFG subcommand
#[derive(Debug, Clone, Parser, Builder)]
#[clap(
about = "Generate a visual control flow graph for EVM bytecode",
Expand Down Expand Up @@ -43,12 +44,14 @@ pub struct CfgArgs {
}

impl CfgArgs {
/// Get the bytecode for the target
pub async fn get_bytecode(&self) -> Result<Vec<u8>> {
get_bytecode_from_target(&self.target, &self.rpc_url).await
}
}

impl CfgArgsBuilder {
/// Create a new instance of the [`CfgArgsBuilder`]
pub fn new() -> Self {
Self {
target: Some(String::new()),
Expand Down
3 changes: 3 additions & 0 deletions crates/cfg/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! The CFG module is responsible for generating control-flow graphs from the given
//! contract's source code via symbolic execution.

mod error;

mod core;
Expand Down
10 changes: 9 additions & 1 deletion crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,17 @@ tracing = "0.1.40"
tracing-subscriber = "0.3.18"
eyre = "0.6.12"
alloy-json-abi = "0.8.3"
alloy = { version = "0.3.3", features = ["full", "rpc-types-debug", "rpc-types-trace"] }
alloy = { version = "0.3.3", features = [
"full",
"rpc-types-debug",
"rpc-types-trace",
] }
async-trait = "0.1.51"

[lints]
workspace = true


[[bin]]
name = "heimdall"
path = "src/main.rs"
Expand Down
14 changes: 7 additions & 7 deletions crates/cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use tracing::{level_filters::LevelFilter, Level};

#[derive(Debug, Parser)]
#[clap(name = "heimdall", author = "Jonathan Becker <[email protected]>", version)]
pub struct Arguments {
pub(crate) struct Arguments {
#[clap(subcommand)]
pub sub: Subcommands,

Expand All @@ -34,7 +34,7 @@ pub struct Arguments {
after_help = "For more information, read the wiki: https://jbecker.dev/r/heimdall-rs/wiki"
)]
#[allow(clippy::large_enum_variant)]
pub enum Subcommands {
pub(crate) enum Subcommands {
#[clap(name = "disassemble", about = "Disassemble EVM bytecode to assembly")]
Disassemble(DisassemblerArgs),

Expand Down Expand Up @@ -66,7 +66,7 @@ pub enum Subcommands {
/// The log configuration.
#[derive(Debug, Args)]
#[clap(next_help_heading = "LOGGING")]
pub struct LogArgs {
pub(crate) struct LogArgs {
/// The format to use for logs written to stdout.
#[clap(long = "log.stdout.format", value_name = "FORMAT", global = true, default_value_t = LogFormat::Terminal)]
pub log_stdout_format: LogFormat,
Expand Down Expand Up @@ -115,7 +115,7 @@ impl LogArgs {
}

/// Initializes tracing with the configured options from cli args.
pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
pub(crate) fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
let mut tracer = HeimdallTracer::new();

let stdout = self.layer(self.log_stdout_format, self.log_stdout_filter.clone(), true);
Expand All @@ -132,7 +132,7 @@ impl LogArgs {

/// The color mode for the cli.
#[derive(Debug, Copy, Clone, ValueEnum, Eq, PartialEq)]
pub enum ColorMode {
pub(crate) enum ColorMode {
/// Colors on
Always,
/// Colors on
Expand Down Expand Up @@ -167,7 +167,7 @@ impl FromStr for ColorMode {
/// The verbosity settings for the cli.
#[derive(Debug, Copy, Clone, Args)]
#[clap(next_help_heading = "DISPLAY")]
pub struct Verbosity {
pub(crate) struct Verbosity {
/// Set the minimum log level.
///
/// -v Warnings & Errors
Expand All @@ -185,7 +185,7 @@ pub struct Verbosity {
impl Verbosity {
/// Get the corresponding [Directive] for the given verbosity, or none if the verbosity
/// corresponds to silent.
pub fn directive(&self) -> Directive {
pub(crate) fn directive(&self) -> Directive {
if self.quiet {
LevelFilter::OFF.into()
} else {
Expand Down
3 changes: 3 additions & 0 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! The Heimdall CLI is a command line interface for interacting with Heimdall modules.

pub(crate) mod args;
pub(crate) mod output;

Expand All @@ -19,6 +21,7 @@ use heimdall_core::{
heimdall_disassembler::disassemble, heimdall_dump::dump, heimdall_inspect::inspect,
};

#[allow(clippy::large_stack_frames)]
#[tokio::main]
async fn main() -> Result<()> {
let args = Arguments::parse();
Expand Down
Loading
Loading