These past two weeks, ZooBC finally starts to feel alive.
Transactions are flowing now. Not through a peer-to-peer network yet, not across nodes scattered around the world, but internally, through the validation and execution pipeline. Still, watching transactions enter the system, get checked, executed, or rejected feels like seeing a heartbeat after a long time.
This is the part of a blockchain where everything converges. Consensus, cryptography, balances, permissions, and time all meet here. If transactions are wrong, nothing else matters.
That is why I am spending an almost unreasonable amount of time here, pacing around my lab, coffee getting cold, running the same scenarios again and again until the behavior feels inevitable.
More Than Just Transfers
ZooBC does not have “just transactions”. It has intent.
There are 13 transaction types, each designed to express a specific action in the protocol. Simple transfers are only one small piece of the puzzle. Everything else that makes ZooBC different happens through transactions as well.
Here is the current set:
| Type | Code | Purpose |
|---|---|---|
| SendZBC | 1 | Transfer tokens |
| NodeRegistration | 2 | Register as blocksmith |
| UpdateNodeRegistration | 258 | Update node info |
| RemoveNodeRegistration | 514 | Unregister node |
| ClaimNodeRegistration | 770 | Claim after timeout |
| SetupAccountDataset | 3 | Store account metadata |
| RemoveAccountDataset | 259 | Remove metadata |
| ApprovalEscrow | 4 | Approve/reject escrow |
| MultiSignature | 5 | Multi-party signing |
| LiquidPayment | 6 | Time-locked payment |
| LiquidPaymentStop | 262 | Cancel liquid payment |
| FeeVoteCommit | 7 | Commit fee vote hash |
| FeeVoteReveal | 263 | Reveal fee vote |
Each transaction type has its own validation rules and its own execution logic. Some modify balances. Others modify protocol state. Some depend on timeouts, others on signatures from multiple parties.
This is why a clean transaction architecture matters so much. There is no generic “apply transaction” here. Everything must be explicit.
The Three-Phase Lifecycle in Practice
The three-phase lifecycle I designed earlier is now fully implemented, and for the first time, I can see it working end to end.
Every transaction moves through the same phases:
ApplyUnconfirmed
This phase runs when a transaction enters the mempool. The transaction is validated, signatures are checked, and the spendable balance is reduced. Nothing permanent happens yet, but this step prevents double-spending before block inclusion. Even unconfirmed transactions have consequences.
ApplyConfirmed
This phase runs when the transaction is included in a block. This is where real state changes happen. Tokens move. Nodes are registered. Escrows are created. Metadata is stored. Everything here must be deterministic and replayable.
UndoApplyUnconfirmed
This phase runs when a transaction leaves the mempool without being confirmed, either because it was included in a block or because it was rejected. The spendable balance deduction is reversed cleanly, leaving no residue behind.
Getting this right feels deeply satisfying, because I know exactly how wrong it can go. Fork handling bugs are subtle, silent, and devastating. This structure makes them much harder to introduce by accident.
The Mempool Starts to Matter
The mempool is no longer just a container. It is a system with rules.
Transactions are stored in a priority queue, sorted by fee-per-byte. Higher-paying transactions are selected first for block inclusion. This is simple, predictable, and aligns incentives without overengineering.
There is also a hard cap of 10,000 pending transactions. This is not about fairness, it is about survival. Unlimited mempools are an invitation to memory exhaustion attacks. When the limit is reached, low-priority transactions are rejected early and explicitly.
Again, boring decisions. Deliberate ones.
Better Errors, Better Thinking
One clear improvement over the Go implementation is how errors are handled.
The C++ transaction validator does not just return success or failure. It returns structured error codes. When a transaction is rejected, the caller knows exactly why: invalid signature, insufficient balance, wrong nonce, expired timeout, malformed payload.
This makes debugging easier, but more importantly, it makes behavior explicit. There is no ambiguity. Every rejection has a reason, and that reason is part of the protocol logic, not an afterthought.
Watching the System Breathe
At this point, there is still no network. No peers. No synchronization.
But transactions are moving. Entering. Being validated. Being executed. Being rolled back when they should be.
Sitting in my man cave late at night, watching logs scroll by, I can feel ZooBC starting to breathe again. Slowly. Carefully. On its own terms.
This is the heart of the blockchain, and for the first time in years, it feels healthy.

