dhruvv1402

compiled to wasm32 Β· no bindgen

Three Rust projects,
running in this page.

A proof-of-work blockchain, a Redis client written from the wire protocol up, and a parallel computation CLI. Every demo below executes the actual crate source compiled to WebAssembly β€” not a JavaScript imitation of it.

dhruv@portfolio:~/website wasm32-unknown-unknown
Loading the Rust runtime…
132 tests passing
3 crates, zero warnings
0 unsafe, compiler-enforced
258KB wasm, no dependencies

Project 01 Β· consensus & cryptography

Blockchain-RUST

SHA-256 block sealing over a canonical length-prefixed encoding, a real nonce search against a difficulty target, and validation that re-derives every hash rather than trusting what is stored.

View repository β†—
  • sha2
  • serde
  • proof of work
  • tamper detection
  • no_std-friendly core

Mine a block

The nonce search below is the crate’s own mine_bounded, run in short bursts so the page stays responsive while it works.

live wasm
3
What exactly gets hashed

A block’s hash has to be reproducible byte for byte, on any machine, in any order β€” otherwise two honest nodes derive different hashes from the same block and the chain forks for no reason. So the fields are fed to SHA-256 through a canonical length-prefixed encoding rather than any formatting the language happens to offer.

hasher.update(index.to_be_bytes());
hasher.update(timestamp.to_be_bytes());
hasher.update((transactions.len() as u64).to_be_bytes());
for tx in transactions {
    tx.hash_into(&mut hasher);   // length-prefixed too
}
hasher.update((previous_hash.len() as u64).to_be_bytes());
hasher.update(previous_hash.as_bytes());
hasher.update(nonce.to_be_bytes());

// rendered as hex, so a difficulty target is
// a plain string prefix comparison
hex::encode(hasher.finalize())
// "0000b1d8ddf4f6cf3e27cb81…"

Length-prefixing is what makes it unambiguous: without it, ("ab", "c") and ("a", "bc") hash identically, and a transaction could be rewritten without disturbing the digest. Validation re-derives every hash from scratch rather than trusting the one stored in the block β€” which is why the tamper button above gets caught.

Project 02 Β· network protocols

Redis-Client-RUST

A complete RESP2/RESP3 codec, correct stream framing over TCP, 40+ typed commands, pipelining and a bounded connection pool. No Redis crate is wrapped β€” the protocol is parsed here.

View repository β†—
  • tokio
  • RESP2 / RESP3
  • incremental decoder
  • pipelining
  • connection pool

Protocol explorer

Both panes call the crate’s real Command::encode and resp::decode. The decoder is the same one the TCP client uses.

live wasm

Encode a command

What the client puts on the wire.

Decode a frame

What comes back. Escapes: \r \n \xNN

Why the decoder returns Option

TCP has no message boundaries: one read may hand you half a reply, or three replies at once. So decoding is incremental β€” Ok(None) means β€œa valid prefix, send more bytes”, and success reports exactly how many bytes were consumed so the connection can drain one frame and keep the rest.

pub fn decode(input: &[u8])
    -> Result<Option<(Value, usize)>, ProtocolError>

// Ok(Some((value, n)))  one frame, n bytes consumed
// Ok(None)              incomplete, read more
// Err(..)               malformed or hostile

The test suite feeds every prefix of a multi-frame buffer through the decoder, so a split at any byte offset is proven to be handled. Try the partial preset above to see it.

Project 03 Β· parallelism & correctness

RUSTY-CLI

Parallel computation kernels with checked arithmetic, a Rayon work pool and a configuration layer where flags, files and defaults compose in a defined order.

View repository β†—
  • rayon
  • clap
  • checked arithmetic
  • tokio
  • indicatif

Where 32 bits run out

Drag past roughly 2,300 and the running total crosses u32::MAX. The accumulator is a checked u64, so it keeps going β€” and stays exact.

live wasm
1000
β€”
u32::MAX

Prime counting

Trial division over every integer below the limit β€” real CPU work, timed in your browser. Native builds run this across all cores with Rayon.

live wasm
100,000
β€”

The bridge

Three crates, one module, no bindgen

The three projects are ordinary path dependencies of a small wasm crate, each built with its native-only features switched off. That is the whole trick: every core was written without I/O, so turning off Tokio, Rayon and the system clock leaves working code behind.

blockchain-rust sha2 Β· proof of work redis-client-rust RESP2 / RESP3 codec rusty-cli checked math Β· rayon rust-demos-wasm dispatch(ptr, len) β†’ ptr this page assets/app.js default-features = false JSON 258 KB Β· zero imports
alloc / dealloc β€” the page reserves the request buffer dispatch β€” one JSON entry point for every demo free_result β€” the module hands the response back length-prefixed

Building it yourself

Each project is a standalone crate with its own suite. Nothing here is checked in without passing all three.

# each of the three projects
cargo test
cargo clippy --all-targets -- -D warnings
cargo fmt --all -- --check

# the wasm that powers this page
cargo build --lib --no-default-features \
      --target wasm32-unknown-unknown