Rollback Testing Guide (0.11.0-beta1+)
Yaci DevKit 0.11.0-beta1 introduces comprehensive rollback simulation capabilities for testing blockchain rollback scenarios. There are two distinct approaches to rollback testing, each serving different purposes.
Types of Rollback Testing
1. Consensus-based Rollback
- Commands:
create-forks
,join-forks
- Method: Network isolation through TCP proxy
- Consensus: Goes through proper Ouroboros consensus
- Use case: Realistic rollback testing that mimics real network conditions
2. Database Snapshot Rollback
- Commands:
take-db-snapshot
,rollback-to-db-snapshot
- Method: Uses database snapshots
- Consensus: Does NOT go through Ouroboros consensus
- Use case: Quick state restoration for development testing
Prerequisites
For consensus-based rollback testing, you need a multi-node setup:
create-node --enable-multi-node --block-time 2 -o --start
Block Time Recommendation: Use --block-time 2
for predictable rollback behavior.
Step-by-Step Consensus Rollback Testing
Distribution Reliability: With --block-time 2
and default --stake-ratio-factor 5
, consensus-based rollbacks work reliably in non-Docker Zip and NPM distributions. However, in Docker distribution, rollbacks may not happen reliably and parallel forks sometimes continue without resolving. If using Docker, consider increasing the stake ratio factor or block time for more reliable rollback behavior if required.
Step 1: Create Multi-Node Devnet
# Create a multi-node devnet with 2-second block time
yaci-cli> create-node --enable-multi-node --block-time 2 -o --start
This creates a 3-node cluster where nodes can be isolated for rollback testing.
Step 2: Monitor Initial State
Check the tip across all nodes to verify they're synchronized:
devnet:default> tip
The tip
command will show the current block height, slot, and hash for all 3 nodes in the cluster.
Step 3: Create Network Fork
Simulate a network partition by isolating nodes:
devnet:default> create-forks
This command isolates Node 1 from Node 2 and Node 3 through TCP proxy manipulation:
Before create-forks (Normal Network):
┌────────┐ ┌────────┐ ┌────────┐
│ Node 1 │◄──►│ Node 2 │◄──►│ Node 3 │
│(Stake1)│ │(Stake5)│ │(Stake5)│
└────────┘ └────────┘ └────────┘
▲ ▲ ▲
└─────────────┼─────────────┘
All nodes synchronized
After create-forks (Network Partition):
┌────────┐ ┌────────┐ ┌────────┐
│ Node 1 │ ✗ │ Node 2 │◄──►│ Node 3 │
│(Stake1)│ │(Stake5)│ │(Stake5)│
└────────┘ └────────┘ └────────┘
│ │ │
Fork A Fork B (Higher Stake)
The network partition:
- Isolates Node 1: Creates Fork A with lower stake (1x)
- Connects Node 2 & 3: Creates Fork B with higher stake (5x + 5x = 10x total)
- TCP Proxy: Blocks communication between the two groups without stopping nodes
- Competing Chains: Both sides continue producing blocks independently
Step 4: Submit Transactions During Fork
While the network is forked, you can submit transactions that will be subject to rollback:
# Submit transactions - these will go through Node 1 (Fork A)
devnet:default> topup addr_test1qrzufj3g0ua489yt235wtc3mrjrlucww2tqdnt7kt5rs09grsag6vxw5v053atks5a6whke03cf2qx3h3g2nhsmzwv3sgml3ed 1000
# Check individual node states
devnet:default> tip
Important: Any transactions submitted during the fork will be processed by Node 1 (the isolated node with lower stake). Since Node 1's chain (Fork A) will likely be rolled back when the networks rejoin, these transactions will be rolled back and may not appear in the final chain.
You'll notice that:
- Node 1 tip: Shows different block height/hash (includes your transactions)
- Node 2 & 3 tips: Show identical block height/hash (synchronized with each other)
- Divergent chains: The different tips indicate the nodes are processing blocks independently
Step 5: Monitor Fork Progress
Continue monitoring the network state:
# Check tips periodically to see divergence
devnet:default> tip
# Submit more transactions if needed for testing
devnet:default> topup <another-address> 500
Step 6: Rejoin Network (Trigger Rollback)
Reconnect the isolated nodes to trigger consensus-based rollback:
devnet:default> join-forks
This command:
- Re-enables network connectivity between nodes
- Triggers Ouroboros consensus protocol
- Causes nodes to resolve chain conflicts
- Results in rollback of transactions on the shorter chain
Step 7: Verify Rollback Results
After rejoining, monitor the network convergence:
# Check that all nodes converge to the same tip
devnet:default> tip
# Verify transaction states
devnet:default> utxos <address-that-received-rolled-back-tx>
Rollback Events: During consensus-based rollbacks, applications listening through N2N (node-to-node) protocols or Ogmios will receive standard rollback events. This allows your applications to properly handle and respond to rollbacks in real-time, making the testing environment realistic for production scenarios.
Transaction Rollback: Transactions submitted during the fork period may be rolled back as they were included in the losing chain (Node 1).
Yaci Viewer Note: If you are checking for transaction rollbacks in Yaci Viewer, go to the "Transactions" page to verify rollback status. The homepage (live view) may still show rolled-back transactions in the UI even after they've been rolled back from the chain.
Important Considerations
Multi-node Requirement: Rollback commands (create-forks
, join-forks
) are only available when using --enable-multi-node
flag. They cannot be used in single-node setups.
Testing Applications: Use rollback testing to verify that your applications properly handle:
- Transaction rollbacks
- State inconsistencies during forks
- Recovery after network partitions
- UTxO availability changes
Troubleshooting
If rollback testing doesn't work as expected:
- Ensure you used
--enable-multi-node
when creating the node - Verify
--block-time 2
or more for predictable behavior - Use
tip
command to monitor node states throughout the process - Check that all services are running properly before starting tests
Fork Not Joining After join-forks
Sometimes forks may not rejoin after executing join-forks
because Ouroboros cannot decide between competing chains and considers both forks valid. If this happens, try:
Option 1: Increase Block Time
# Use longer block time for more predictable consensus
yaci-cli> create-node --enable-multi-node --block-time 5 -o --start
Option 2: Adjust Stake Ratio Factor
The --stake-ratio-factor
defines the staking ratio between nodes for realistic network scenarios. By default, it's set to 5, meaning nodes 2 and 3 have 5 times more stake than node 1.
# Increase stake ratio to make consensus more decisive
yaci-cli> create-node --enable-multi-node --block-time 2 --stake-ratio-factor 10 -o --start
# For NPM distribution
yaci-devkit up --enable-multi-node --block-time 2 --stake-ratio-factor 10 --interactive
A higher stake ratio factor (e.g., 10) gives nodes 2 and 3 even more staking power, making it more likely that their chain will be chosen during consensus resolution.
Database Snapshot Rollback (Alternative Method)
For simpler state restoration without consensus involvement:
Step 1: Take Database Snapshot
devnet:default> take-db-snapshot
Step 2: Perform Operations
# Submit transactions or perform other operations
devnet:default> topup addr_test1... 1000
devnet:default> tip
Step 3: Rollback to Database Snapshot
devnet:default> rollback-to-db-snapshot
This instantly restores the database to the previously taken snapshot without involving network consensus.