DEV Community

Cover image for I Built a P2P Blockchain in Rust — Here’s What Actually Went Wrong
Sandeep Sarkar
Sandeep Sarkar

Posted on

I Built a P2P Blockchain in Rust — Here’s What Actually Went Wrong

Introduction

I recently finished building Toki, a peer-to-peer blockchain written entirely in Rust.
This wasn’t a tutorial project or a copy-paste experiment—it was an attempt to truly understand how blockchains and P2P systems work under the hood.

What I didn’t expect was how many things would break along the way.

This post isn’t a step-by-step tutorial.
It’s a developer log about what I built, what failed, and what I learned by fixing it.


What is Toki?

Toki is an educational blockchain node with the following features:

  • Proof-of-Work consensus
  • Wallets using Ed25519 public/private key cryptography
  • Signed transactions and a mempool
  • Block mining and validation
  • libp2p-based peer-to-peer networking
  • Gossip-based transaction and block propagation
  • Multi-node support (real nodes, real connections)

Each node runs independently and communicates over a P2P network—no central server.

GitHub repo:
👉 https://github.com/sandy4242/Toki


Wallets & Transactions

Each wallet generates an Ed25519 keypair.
Transactions are signed using the private key and verified using the public key before being accepted.

This immediately taught me an important lesson:

If your transaction format or hashing logic is even slightly inconsistent, nothing works.

Rust’s strict typing helped catch mistakes early—but cryptography is unforgiving by design.


Mining & Blocks

Mining uses a simple Proof-of-Work mechanism:

  • Increment a nonce
  • Hash the block
  • Check for a required number of leading zeros

It’s intentionally simple, but even here, small design choices matter:

  • What exactly goes into the hash?
  • How are transactions serialized?
  • When is a block considered valid?

Getting deterministic hashing across nodes was harder than expected.


The Hard Part: P2P Networking

This is where most of my time went.

I used libp2p, the same networking stack used by IPFS and Polkadot.
It’s powerful—but very strict.

Some things that tripped me up:

  • SwarmConfig vs swarm::Config (API changes)
  • Futures not in scope (libp2p::futures::StreamExt)
  • Async event loops that look “stuck” but are actually idle
  • Multiaddr formatting (/p2p/<PeerId> is mandatory)
  • Nodes don’t auto-connect—you must explicitly dial peers

At one point, both nodes were running perfectly…
and doing absolutely nothing.

Because in P2P systems:

If no one connects, nothing happens.

Once I understood that mental model, things finally clicked.


Gossip, Mempool, and Sync

Transactions are:

  1. Created on one node
  2. Gossiped to peers
  3. Validated and added to the mempool
  4. Mined into a block
  5. Broadcast back to the network

To avoid infinite rebroadcast loops, I had to implement basic message de-duplication.

This was the moment Toki stopped being “code” and started behaving like a networked system.


A Quick Git Lesson (Yes, Really)

At one point, I accidentally committed the entire target/ directory.

Thousands of files.
GitHub repo size exploded.

Lesson learned:

  • .gitignore does not untrack files
  • You must git rm --cached them
  • Nested project directories make this worse

Not blockchain-related—but very real.


What I Learned

  • P2P systems are event-driven, not request-driven
  • libp2p is powerful but demands correctness
  • Rust forces you to think clearly about ownership and state
  • Cryptography has zero tolerance for “almost right”
  • If your system compiles and connects, you’re probably doing something right

What’s Next for Toki?

This is v1.0. Planned next steps include:

  • Longest-chain fork resolution
  • Account balances and state tracking
  • Bootstrap nodes
  • Persistent storage
  • Better CLI tooling

Final Thoughts

Building Toki taught me more about distributed systems than any tutorial ever could.

If you’re learning Rust, blockchain, or P2P networking—my advice is simple:

Build something that breaks. Then fix it.

That’s where the real learning happens.

Thanks for reading

~sandy

Top comments (1)

Collapse
 
scottcjn profile image
AutoJanitor

Really enjoyed this writeup — the "both nodes running perfectly and doing absolutely nothing" moment is painfully relatable. That mental model shift from request-driven to event-driven is the real initiation into P2P.

We hit similar walls building RustChain (also uses Ed25519 for wallets). One thing that changed our trajectory: we moved away from PoW entirely and went with Proof-of-Antiquity — hardware attestation where the age and type of your silicon determines your mining weight. A 1975 6502 earns more than a modern Threadripper. It flips the "who has more compute" game on its head.

The deterministic hashing across nodes problem you mention is something we solved differently too — instead of trying to get perfect serialization parity, we do round-robin 1-CPU-1-Vote consensus where each attested machine gets one vote weighted by hardware class. No hash race, no forks from serialization drift.

Your libp2p struggles remind me why we went with a simpler attestation model — nodes submit hardware fingerprints (timing jitter, cache profiles, SIMD signatures) to prove they're real silicon, not VMs. Sybil resistance through physics rather than computation.

Curious if you've thought about what happens when someone spins up 100 Toki nodes on AWS? That's the problem that pushed us toward hardware identity. Would love to hear your take.

Toki's a solid foundation — the fact that you got multi-node gossip working from scratch in Rust is no small feat.